diff --git a/.changeset/cool-bears-marry.md b/.changeset/cool-bears-marry.md new file mode 100644 index 0000000..612d048 --- /dev/null +++ b/.changeset/cool-bears-marry.md @@ -0,0 +1,5 @@ +--- +'gov4git-desktop-app': minor +--- + +Add community dashboard diff --git a/src/renderer/src/pages/dashboard/community/CommunityBreadcrumbs.tsx b/src/renderer/src/pages/dashboard/community/CommunityBreadcrumbs.tsx new file mode 100644 index 0000000..ad2426d --- /dev/null +++ b/src/renderer/src/pages/dashboard/community/CommunityBreadcrumbs.tsx @@ -0,0 +1,69 @@ +import { + Breadcrumb, + BreadcrumbButton, + BreadcrumbDivider, + BreadcrumbItem, +} from '@fluentui/react-components' +import { FC, memo, useMemo } from 'react' + +import { useDataStore } from '../../../store/store.js' + +export const CommunityBreadcrumbs: FC = memo(function CommunityBreadcrumbs() { + const state = useDataStore((s) => s.communityDashboard.state) + const setCommunityDashboardState = useDataStore( + (s) => s.communityDashboard.setState, + ) + const setCommunityManageState = useDataStore( + (s) => s.communityManage.setState, + ) + const communityManageState = useDataStore((s) => s.communityManage.state) + const communityToManage = useDataStore( + (s) => s.communityManage.communityToManage, + ) + + const communityManageMessage = useMemo(() => { + switch (communityManageState) { + case 'users': + return 'Users' + case 'issues': + return 'Issues' + case 'pull-requests': + return 'Pull Requests' + default: + return '' + } + }, [communityManageState]) + + if (state == 'overview') { + return <> + } + + return ( + + + { + setCommunityDashboardState('overview') + setCommunityManageState('overview') + }} + > + Communities + + + + + setCommunityManageState('overview')}> + Manage {communityToManage?.name} + + + {communityManageMessage !== '' && ( + <> + + + {communityManageMessage} + + + )} + + ) +}) diff --git a/src/renderer/src/pages/dashboard/community/DashboardCommunity.styles.ts b/src/renderer/src/pages/dashboard/community/DashboardCommunity.styles.ts deleted file mode 100644 index 39a7fe7..0000000 --- a/src/renderer/src/pages/dashboard/community/DashboardCommunity.styles.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { makeStyles, shorthands } from '@fluentui/react-components' - -export const useDashboardCommunityStyle = makeStyles({ - buttonRow: { - display: 'flex', - ...shorthands.gap('8px'), - alignItems: 'center', - }, -}) diff --git a/src/renderer/src/pages/dashboard/community/DashboardCommunity.tsx b/src/renderer/src/pages/dashboard/community/DashboardCommunity.tsx index 6731932..14e01bb 100644 --- a/src/renderer/src/pages/dashboard/community/DashboardCommunity.tsx +++ b/src/renderer/src/pages/dashboard/community/DashboardCommunity.tsx @@ -1,15 +1,11 @@ -import { Button } from '@fluentui/react-components' -import { Add32Filled, People32Filled } from '@fluentui/react-icons' import { FC, memo, useMemo } from 'react' import { StyledCard } from '../../../components/index.js' import { useDataStore } from '../../../store/store.js' import { useHeadingsStyles } from '../../../styles/index.js' -import { CommunityTable } from './CommunityTable.js' -import { useDashboardCommunityStyle } from './DashboardCommunity.styles.js' -import { CommunityDeploy } from './deploy/CommunityDeploy.js' -import { JoinCommunity } from './join/JoinCommunity.js' +import { CommunityBreadcrumbs } from './CommunityBreadcrumbs.js' import { ManageCommunity } from './manage/ManageCommunity.js' +import { CommunityOverview } from './overview/CommunityOverview.js' export const DashboardCommunity: FC = memo(function DashboardCommunity() { const headerStyles = useHeadingsStyles() @@ -19,14 +15,10 @@ export const DashboardCommunity: FC = memo(function DashboardCommunity() { const Component: FC = useMemo(() => { switch (communityDashboardState) { - case 'join': - return JoinCommunity - case 'deploy': - return CommunityDeploy + case 'overview': + return CommunityOverview case 'manage': return ManageCommunity - default: - return DashboardCommunityButtons } }, [communityDashboardState]) @@ -34,38 +26,9 @@ export const DashboardCommunity: FC = memo(function DashboardCommunity() { <>

Communities

- -
+
) }) - -export const DashboardCommunityButtons: FC = memo( - function DashboardCommunityButtons() { - const styles = useDashboardCommunityStyle() - const setCommunityDashboardState = useDataStore( - (s) => s.communityDashboard.setState, - ) - - return ( -
- - -
- ) - }, -) diff --git a/src/renderer/src/pages/dashboard/community/manage/ManageCommunity.tsx b/src/renderer/src/pages/dashboard/community/manage/ManageCommunity.tsx index 914c5fe..e9cd6f4 100644 --- a/src/renderer/src/pages/dashboard/community/manage/ManageCommunity.tsx +++ b/src/renderer/src/pages/dashboard/community/manage/ManageCommunity.tsx @@ -1,54 +1,27 @@ -import { Tab, TabList } from '@fluentui/react-components' -import { type FC, memo, useMemo, useState } from 'react' +import { type FC, memo, useMemo } from 'react' -import { Loader } from '../../../../components/Loader.js' import { useDataStore } from '../../../../store/store.js' -import { IssuesPanel } from './IssuesPanel.js' -import { UserPanel } from './UserPanel.js' +import { IssuesPanel } from './issues/IssuesPanel.js' +import { ManageCommunityOverview } from './overview/ManageCommunityOverview.js' +import { UserPanel } from './users/UserPanel.js' export const ManageCommunity: FC = memo(function ManageCommunity() { - const [selectedTab, setSelectedTab] = useState('users') - const selectedCommunity = useDataStore( - (s) => s.communityManage.communityToManage, - )! - const managedUsers = useDataStore((s) => s.communityManage.users) - const managedIssues = useDataStore((s) => s.communityManage.issues) - const loading = useMemo(() => { - return managedUsers == null || managedIssues == null - }, [managedUsers, managedIssues]) + const communityManageState = useDataStore((s) => s.communityManage.state) const Component: FC = useMemo(() => { - if (selectedTab === 'issues') { - return IssuesPanel + switch (communityManageState) { + case 'users': + return UserPanel + case 'issues': + return IssuesPanel + default: + return ManageCommunityOverview } - - return UserPanel - }, [selectedTab]) + }, [communityManageState]) return ( <> -

Manage {selectedCommunity.name}

-
- setSelectedTab(d.value as string)} - > - - Users - - - Issues - - - Pull Requests - - -
- - - -
-
+ ) }) diff --git a/src/renderer/src/pages/dashboard/community/manage/IssuesPanel.tsx b/src/renderer/src/pages/dashboard/community/manage/issues/IssuesPanel.tsx similarity index 89% rename from src/renderer/src/pages/dashboard/community/manage/IssuesPanel.tsx rename to src/renderer/src/pages/dashboard/community/manage/issues/IssuesPanel.tsx index 3f5f6ff..d137309 100644 --- a/src/renderer/src/pages/dashboard/community/manage/IssuesPanel.tsx +++ b/src/renderer/src/pages/dashboard/community/manage/issues/IssuesPanel.tsx @@ -12,12 +12,13 @@ import { import { parse } from 'marked' import { type FC, memo, useCallback, useState } from 'react' -import { Policy } from '../../../../../../electron/db/schema.js' -import type { CommunityIssue } from '../../../../../../electron/services/index.js' -import { Message } from '../../../../components/Message.js' -import { useDataStore } from '../../../../store/store.js' -import { useMessageStyles } from '../../../../styles/messages.js' -import { useManageCommunityStyles } from './styles.js' +import { Policy } from '../../../../../../../electron/db/schema.js' +import type { CommunityIssue } from '../../../../../../../electron/services/index.js' +import { Loader } from '../../../../../components/Loader.js' +import { Message } from '../../../../../components/Message.js' +import { useDataStore } from '../../../../../store/store.js' +import { useMessageStyles } from '../../../../../styles/messages.js' +import { useManageCommunityStyles } from '../styles.js' const isManaged = (issue: CommunityIssue): boolean => { return issue.policy != null @@ -37,6 +38,7 @@ export const IssuesPanel: FC = memo(function IssuesPanel() { const [selectedPolicy, setSelectedPolicy] = useState(null) const manageIssue = useDataStore((s) => s.communityManage.manageIssue) const issues = useDataStore((s) => s.communityManage.issues) + const issuesLoading = useDataStore((s) => s.communityManage.issuesLoading) const selectPolicy = useCallback( (policy: Policy) => { @@ -83,7 +85,7 @@ export const IssuesPanel: FC = memo(function IssuesPanel() { }, [setSuccessMessage]) return ( -
+
@@ -171,6 +173,6 @@ export const IssuesPanel: FC = memo(function IssuesPanel() { )} )} - + ) }) diff --git a/src/renderer/src/pages/dashboard/community/manage/overview/ManageCommunityOverview.styles.ts b/src/renderer/src/pages/dashboard/community/manage/overview/ManageCommunityOverview.styles.ts new file mode 100644 index 0000000..d8a9b33 --- /dev/null +++ b/src/renderer/src/pages/dashboard/community/manage/overview/ManageCommunityOverview.styles.ts @@ -0,0 +1,21 @@ +import { makeStyles, shorthands } from '@fluentui/react-components' + +import { gov4GitTokens } from '../../../../../App/theme/tokens.js' + +export const useManageCommunityOverviewStyles = makeStyles({ + container: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', + ...shorthands.gap('16px', '28px'), + }, + card: { + ...shorthands.borderRadius(gov4GitTokens.borderRadiusXLarge), + boxShadow: gov4GitTokens.shadow16, + ...shorthands.border('1px', 'solid', gov4GitTokens.g4gColorNeutralDark), + ':hover': { + boxShadow: gov4GitTokens.shadow28, + backgroundColor: gov4GitTokens.g4gColorSecondaryGreen1, + cursor: 'pointer', + }, + }, +}) diff --git a/src/renderer/src/pages/dashboard/community/manage/overview/ManageCommunityOverview.tsx b/src/renderer/src/pages/dashboard/community/manage/overview/ManageCommunityOverview.tsx new file mode 100644 index 0000000..ec49ea5 --- /dev/null +++ b/src/renderer/src/pages/dashboard/community/manage/overview/ManageCommunityOverview.tsx @@ -0,0 +1,50 @@ +import { Card } from '@fluentui/react-components' +import { FC } from 'react' + +import { useDataStore } from '../../../../../store/store.js' +import { useManageCommunityOverviewStyles } from './ManageCommunityOverview.styles.js' + +export const ManageCommunityOverview: FC = function ManageCommunityOverview() { + const styles = useManageCommunityOverviewStyles() + const setCommunityManageState = useDataStore( + (s) => s.communityManage.setState, + ) + + return ( +
+ setCommunityManageState('users')} + > +

Manage Users

+
+ View active users, issue voting credits, and manage requests to join + the community. +
+
+ setCommunityManageState('issues')} + > +

Manage Issues

+
+ View all issues for selected GitHub community. Select issues to be + managed by Gov4Git. +
+
+ setCommunityManageState('pull-requests')} + > +

Manage Pull Requests

+
+ View all pull requests for selected GitHub community. Select pull + requests to be managed by Gov4Git. +
+
+
+ ) +} diff --git a/src/renderer/src/pages/dashboard/community/manage/UserPanel.tsx b/src/renderer/src/pages/dashboard/community/manage/users/UserPanel.tsx similarity index 91% rename from src/renderer/src/pages/dashboard/community/manage/UserPanel.tsx rename to src/renderer/src/pages/dashboard/community/manage/users/UserPanel.tsx index b4072c7..b917455 100644 --- a/src/renderer/src/pages/dashboard/community/manage/UserPanel.tsx +++ b/src/renderer/src/pages/dashboard/community/manage/users/UserPanel.tsx @@ -11,8 +11,9 @@ import { } from '@fluentui/react-components' import { type FC, FormEvent, memo, useCallback, useState } from 'react' -import { useDataStore } from '../../../../store/store.js' -import { useManageCommunityStyles } from './styles.js' +import { Loader } from '../../../../../components/Loader.js' +import { useDataStore } from '../../../../../store/store.js' +import { useManageCommunityStyles } from '../styles.js' export const UserPanel: FC = memo(function UserPanel() { const selectedCommunity = useDataStore( @@ -26,6 +27,7 @@ export const UserPanel: FC = memo(function UserPanel() { const [loading, setLoading] = useState(false) const styles = useManageCommunityStyles() const users = useDataStore((s) => s.communityManage.users) + const usersLoading = useDataStore((s) => s.communityManage.usersLoading) const issueCredits = useCallback( async (ev: FormEvent) => { @@ -52,7 +54,7 @@ export const UserPanel: FC = memo(function UserPanel() { ) return ( - <> +
@@ -111,6 +113,6 @@ export const UserPanel: FC = memo(function UserPanel() { )} - + ) }) diff --git a/src/renderer/src/pages/dashboard/community/overview/CommunityOverview.styles.ts b/src/renderer/src/pages/dashboard/community/overview/CommunityOverview.styles.ts new file mode 100644 index 0000000..c8802e1 --- /dev/null +++ b/src/renderer/src/pages/dashboard/community/overview/CommunityOverview.styles.ts @@ -0,0 +1,13 @@ +import { makeStyles, shorthands } from '@fluentui/react-components' + +export const useCommunityOverviewStyles = makeStyles({ + container: { + display: 'grid', + ...shorthands.gap('28px'), + }, + buttonRow: { + display: 'flex', + ...shorthands.gap('16px'), + alignItems: 'center', + }, +}) diff --git a/src/renderer/src/pages/dashboard/community/overview/CommunityOverview.tsx b/src/renderer/src/pages/dashboard/community/overview/CommunityOverview.tsx new file mode 100644 index 0000000..63428cd --- /dev/null +++ b/src/renderer/src/pages/dashboard/community/overview/CommunityOverview.tsx @@ -0,0 +1,60 @@ +import { Button } from '@fluentui/react-components' +import { Add32Filled, People32Filled } from '@fluentui/react-icons' +import { FC, memo, useMemo } from 'react' + +import { useDataStore } from '../../../../store/store.js' +import { useCommunityOverviewStyles } from './CommunityOverview.styles.js' +import { CommunityTable } from './CommunityTable.js' +import { CommunityDeploy } from './deploy/CommunityDeploy.js' +import { JoinCommunity } from './join/JoinCommunity.js' + +export const CommunityOverview: FC = function CommunityOverview() { + const styles = useCommunityOverviewStyles() + const communityOverviewState = useDataStore((s) => s.communityOverview.state) + + const Component: FC = useMemo(() => { + switch (communityOverviewState) { + case 'join': + return JoinCommunity + case 'deploy': + return CommunityDeploy + default: + return CommunityOverviewButtons + } + }, [communityOverviewState]) + + return ( +
+ + +
+ ) +} + +export const CommunityOverviewButtons: FC = memo( + function CommunityOverviewButtons() { + const styles = useCommunityOverviewStyles() + const setCommunityOverviewState = useDataStore( + (s) => s.communityOverview.setState, + ) + + return ( +
+ + +
+ ) + }, +) diff --git a/src/renderer/src/pages/dashboard/community/CommunityTable.styles.ts b/src/renderer/src/pages/dashboard/community/overview/CommunityTable.styles.ts similarity index 87% rename from src/renderer/src/pages/dashboard/community/CommunityTable.styles.ts rename to src/renderer/src/pages/dashboard/community/overview/CommunityTable.styles.ts index 9a23d93..dc85976 100644 --- a/src/renderer/src/pages/dashboard/community/CommunityTable.styles.ts +++ b/src/renderer/src/pages/dashboard/community/overview/CommunityTable.styles.ts @@ -1,6 +1,6 @@ import { makeStyles, shorthands } from '@fluentui/react-components' -import { gov4GitTokens } from '../../../App/theme/index.js' +import { gov4GitTokens } from '../../../../App/theme/index.js' export const useCommunityTableStyle = makeStyles({ formRow: { diff --git a/src/renderer/src/pages/dashboard/community/CommunityTable.tsx b/src/renderer/src/pages/dashboard/community/overview/CommunityTable.tsx similarity index 93% rename from src/renderer/src/pages/dashboard/community/CommunityTable.tsx rename to src/renderer/src/pages/dashboard/community/overview/CommunityTable.tsx index 2398b33..a2d4b90 100644 --- a/src/renderer/src/pages/dashboard/community/CommunityTable.tsx +++ b/src/renderer/src/pages/dashboard/community/overview/CommunityTable.tsx @@ -10,12 +10,14 @@ import { } from '@fluentui/react-components' import { FC, useCallback, useEffect, useMemo, useState } from 'react' -import type { Community } from '../../../../../electron/db/schema.js' -import { useDataStore } from '../../../store/store.js' +import type { Community } from '../../../../../../electron/db/schema.js' +import { Loader } from '../../../../components/Loader.js' +import { useDataStore } from '../../../../store/store.js' import { useCommunityTableStyle } from './CommunityTable.styles.js' export const CommunityTable: FC = function CommunityTable() { const communities = useDataStore((s) => s.communityInfo.communities) + const communitiesLoading = useDataStore((s) => s.communityInfo.loading) const selectedCommunity = useDataStore( (s) => s.communityInfo.selectedCommunity, ) @@ -55,7 +57,7 @@ export const CommunityTable: FC = function CommunityTable() { }, [communities, setCommunityPages]) return ( - <> + {communities != null && communities.length > 0 && (
@@ -94,7 +96,7 @@ export const CommunityTable: FC = function CommunityTable() {
)} - + ) } diff --git a/src/renderer/src/pages/dashboard/community/deploy/CommunityDeploy.tsx b/src/renderer/src/pages/dashboard/community/overview/deploy/CommunityDeploy.tsx similarity index 92% rename from src/renderer/src/pages/dashboard/community/deploy/CommunityDeploy.tsx rename to src/renderer/src/pages/dashboard/community/overview/deploy/CommunityDeploy.tsx index f99875f..c7bf7bd 100644 --- a/src/renderer/src/pages/dashboard/community/deploy/CommunityDeploy.tsx +++ b/src/renderer/src/pages/dashboard/community/overview/deploy/CommunityDeploy.tsx @@ -1,6 +1,6 @@ import { FC, memo, useMemo } from 'react' -import { useDataStore } from '../../../../store/store.js' +import { useDataStore } from '../../../../../store/store.js' import { Nav } from './Nav.js' import { ReviewDeploy } from './ReviewDeploy.js' import { SelectOrg } from './SelectOrg.js' diff --git a/src/renderer/src/pages/dashboard/community/deploy/Nav.tsx b/src/renderer/src/pages/dashboard/community/overview/deploy/Nav.tsx similarity index 95% rename from src/renderer/src/pages/dashboard/community/deploy/Nav.tsx rename to src/renderer/src/pages/dashboard/community/overview/deploy/Nav.tsx index 632869c..c1dc39a 100644 --- a/src/renderer/src/pages/dashboard/community/deploy/Nav.tsx +++ b/src/renderer/src/pages/dashboard/community/overview/deploy/Nav.tsx @@ -6,7 +6,7 @@ import { } from '@fluentui/react-components' import { FC, memo } from 'react' -import { useDataStore } from '../../../../store/store.js' +import { useDataStore } from '../../../../../store/store.js' export const Nav: FC = memo(function Nav() { const state = useDataStore((s) => s.communityDeploy.state) diff --git a/src/renderer/src/pages/dashboard/community/deploy/ReviewDeploy.tsx b/src/renderer/src/pages/dashboard/community/overview/deploy/ReviewDeploy.tsx similarity index 85% rename from src/renderer/src/pages/dashboard/community/deploy/ReviewDeploy.tsx rename to src/renderer/src/pages/dashboard/community/overview/deploy/ReviewDeploy.tsx index 5f339bc..211b56a 100644 --- a/src/renderer/src/pages/dashboard/community/deploy/ReviewDeploy.tsx +++ b/src/renderer/src/pages/dashboard/community/overview/deploy/ReviewDeploy.tsx @@ -1,14 +1,14 @@ import { Button } from '@fluentui/react-components' import { FC, memo, useCallback, useState } from 'react' -import { Message } from '../../../../components/Message.js' -import { useDataStore } from '../../../../store/store.js' -import { useMessageStyles } from '../../../../styles/index.js' +import { Message } from '../../../../../components/Message.js' +import { useDataStore } from '../../../../../store/store.js' +import { useMessageStyles } from '../../../../../styles/index.js' export const ReviewDeploy: FC = memo(function ReviewDeploy() { const [loading, setLoading] = useState(false) - const setCommunityDashboardState = useDataStore( - (s) => s.communityDashboard.setState, + const setCommunityOverviewState = useDataStore( + (s) => s.communityOverview.setState, ) const setCommunityDeployState = useDataStore( (s) => s.communityDeploy.setState, @@ -42,8 +42,8 @@ export const ReviewDeploy: FC = memo(function ReviewDeploy() { const dismissMessage = useCallback(() => { setSuccessMessage('') setCommunityDeployState('initial') - setCommunityDashboardState('initial') - }, [setSuccessMessage, setCommunityDashboardState, setCommunityDeployState]) + setCommunityOverviewState('overview') + }, [setSuccessMessage, setCommunityOverviewState, setCommunityDeployState]) return ( <> diff --git a/src/renderer/src/pages/dashboard/community/deploy/SelectOrg.tsx b/src/renderer/src/pages/dashboard/community/overview/deploy/SelectOrg.tsx similarity index 90% rename from src/renderer/src/pages/dashboard/community/deploy/SelectOrg.tsx rename to src/renderer/src/pages/dashboard/community/overview/deploy/SelectOrg.tsx index 22b6c67..70bfa70 100644 --- a/src/renderer/src/pages/dashboard/community/deploy/SelectOrg.tsx +++ b/src/renderer/src/pages/dashboard/community/overview/deploy/SelectOrg.tsx @@ -13,10 +13,10 @@ import { import { Verification } from '@octokit/auth-oauth-device/dist-types/types.js' import { FC, memo, useCallback, useEffect, useState } from 'react' -import { Loader } from '../../../../components/Loader.js' -import { LoginVerification } from '../../../../components/LoginVerification.js' -import { useDataStore } from '../../../../store/store.js' -import { useButtonStyles } from '../../../../styles/index.js' +import { Loader } from '../../../../../components/Loader.js' +import { LoginVerification } from '../../../../../components/LoginVerification.js' +import { useDataStore } from '../../../../../store/store.js' +import { useButtonStyles } from '../../../../../styles/index.js' import { useCommunityDeployStyle } from './styles.js' export const SelectOrg: FC = memo(function SelectOrg() { @@ -26,8 +26,8 @@ export const SelectOrg: FC = memo(function SelectOrg() { const selectedOrg = useDataStore((s) => s.communityDeploy.selectedOrg) const setSelectedOrg = useDataStore((s) => s.communityDeploy.selectOrg) const styles = useCommunityDeployStyle() - const setCommunityDashboardState = useDataStore( - (s) => s.communityDashboard.setState, + const setCommunityOverviewState = useDataStore( + (s) => s.communityOverview.setState, ) const setCommunityDeployState = useDataStore( (s) => s.communityDeploy.setState, @@ -108,7 +108,7 @@ export const SelectOrg: FC = memo(function SelectOrg() { )}
-