Skip to content
This repository was archived by the owner on Dec 6, 2025. It is now read-only.

Commit 176e0e0

Browse files
authored
feat: add team page (#1195)
1 parent 87f4dc3 commit 176e0e0

4 files changed

Lines changed: 198 additions & 2 deletions

File tree

customer/api/dataclasses/user_seat.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1+
import type { Translation } from '@helpwave/common/hooks/useTranslation'
2+
3+
export const userRoles = ['admin' , 'user'] as const
4+
15
/**
26
* Defines the possible roles a user can have.
37
*/
4-
export type UserRole = 'admin' | 'user';
8+
export type UserRole = typeof userRoles[number]
9+
10+
export type UserRoleTranslation = Record<UserRole, string>
11+
12+
export const defaultUserRoleTranslation: Translation<UserRoleTranslation> = {
13+
en: {
14+
user: 'User',
15+
admin: 'Admin'
16+
},
17+
de: {
18+
user: 'Nutzer',
19+
admin: 'Admin'
20+
}
21+
}
522

623
/**
724
* Represents a user seat in the system.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
2+
import { QueryKeys } from '@/api/mutations/query_keys'
3+
import type { UserSeat } from '@/api/dataclasses/user_seat'
4+
5+
// TODO delete later
6+
const userSeatData: UserSeat[] = [
7+
{
8+
customerUUID: 'customer',
9+
email: 'test1@helpwave.de',
10+
firstName: 'Max',
11+
lastName: 'Mustermann',
12+
role: 'admin',
13+
enabled: true
14+
},
15+
{
16+
customerUUID: 'customer',
17+
email: 'test2@helpwave.de',
18+
firstName: 'Mary',
19+
lastName: 'Jane',
20+
role: 'admin',
21+
enabled: true
22+
},
23+
{
24+
customerUUID: 'customer',
25+
email: 'test3@helpwave.de',
26+
firstName: 'Maxine',
27+
lastName: 'Mustermann',
28+
role: 'user',
29+
enabled: true
30+
},
31+
{
32+
customerUUID: 'customer',
33+
email: 'test4@helpwave.de',
34+
firstName: 'John',
35+
lastName: 'Doe',
36+
role: 'user',
37+
enabled: false
38+
},
39+
{
40+
customerUUID: 'customer',
41+
email: 'test5@helpwave.de',
42+
firstName: 'Peter',
43+
lastName: 'Parker',
44+
role: 'user',
45+
enabled: false
46+
},
47+
]
48+
49+
export const useUserSeatsQuery = () => {
50+
return useQuery({
51+
queryKey: [QueryKeys.userSeat, 'all'],
52+
queryFn: async () => {
53+
// TODO do request here with auth data
54+
return userSeatData
55+
},
56+
})
57+
}
58+
59+
export const useUserSeatUpdateMutation = () => {
60+
const queryClient = useQueryClient()
61+
return useMutation({
62+
mutationFn: async (userSeat: UserSeat) => {
63+
// TODO do request here
64+
65+
const index = userSeatData.findIndex(e => e.customerUUID === userSeat.customerUUID && e.email === userSeat.email)
66+
if (index === -1) {
67+
throw 'User Seat not found'
68+
}
69+
userSeatData[index] = userSeat
70+
71+
return userSeat
72+
},
73+
onSuccess: () => {
74+
queryClient.refetchQueries([QueryKeys.customer]).catch(reason => console.error(reason))
75+
}
76+
})
77+
}

customer/components/layout/Page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Link from 'next/link'
1010
import { Helpwave } from '@helpwave/common/icons/Helpwave'
1111
import type { Languages } from '@helpwave/common/hooks/useLanguage'
1212
import { useTranslation } from '@helpwave/common/hooks/useTranslation'
13-
import { GaugeIcon, Menu, Package, Receipt, Settings } from 'lucide-react'
13+
import { GaugeIcon, Menu, Package, Receipt, Settings, UsersIcon } from 'lucide-react'
1414
import Head from 'next/head'
1515
import { MobileNavigationOverlay } from '@/components/layout/MobileNavigationOverlay'
1616

@@ -34,6 +34,7 @@ export type PageProps = PropsWithChildren<{
3434

3535
const navItems: NavItem[] = [
3636
{ name: { en: 'Dashboard', de: 'dashboard' }, url: '/', icon: (<GaugeIcon size={24}/>) },
37+
{ name: { en: 'Team', de: 'Team' }, url: '/team', icon: (<UsersIcon size={24}/>) },
3738
{ name: { en: 'Products', de: 'Produkte' }, url: '/products', icon: (<Package size={24}/>) },
3839
{ name: { en: 'Invoices', de: 'Rechnungen' }, url: '/invoices', icon: (<Receipt size={24}/>) },
3940
{ name: { en: 'Settings', de: 'Einstellungen' }, url: '/settings', icon: (<Settings size={24}/>) },

customer/pages/team/index.tsx

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import type { NextPage } from 'next'
2+
import type { Languages } from '@helpwave/common/hooks/useLanguage'
3+
import { useTranslation, type PropsForTranslation } from '@helpwave/common/hooks/useTranslation'
4+
import { Page } from '@/components/layout/Page'
5+
import titleWrapper from '@/utils/titleWrapper'
6+
import { Section } from '@/components/layout/Section'
7+
import { LoadingAndErrorComponent } from '@helpwave/common/components/LoadingAndErrorComponent'
8+
import { tw } from '@twind/core'
9+
import type { UserRole, UserRoleTranslation, UserSeat } from '@/api/dataclasses/user_seat'
10+
import { userRoles } from '@/api/dataclasses/user_seat'
11+
import { defaultUserRoleTranslation } from '@/api/dataclasses/user_seat'
12+
import { useUserSeatsQuery, useUserSeatUpdateMutation } from '@/api/mutations/user_seat_mutations'
13+
import { Table } from '@helpwave/common/components/Table'
14+
import { Select } from '@helpwave/common/components/user-input/Select'
15+
import { Checkbox } from '@helpwave/common/components/user-input/Checkbox'
16+
17+
type TeamTranslation = {
18+
team: string,
19+
teamDescription: string,
20+
role : string,
21+
enabled: string,
22+
} & UserRoleTranslation
23+
24+
const defaultTeamTranslations: Record<Languages, TeamTranslation> = {
25+
en: {
26+
...defaultUserRoleTranslation.en,
27+
team: 'Team',
28+
teamDescription: 'Here you can change and add members to your team.',
29+
role: 'Role',
30+
enabled: 'Enabled',
31+
},
32+
de: {
33+
...defaultUserRoleTranslation.de,
34+
team: 'Team',
35+
teamDescription: 'Hier kannst du Mitglieder deines Teams verwalten.',
36+
role: 'Rolle',
37+
enabled: 'Aktiv',
38+
}
39+
}
40+
41+
type TeamServerSideProps = {
42+
jsonFeed: unknown,
43+
}
44+
45+
const Team: NextPage<PropsForTranslation<TeamTranslation, TeamServerSideProps>> = ({ overwriteTranslation }) => {
46+
const translation = useTranslation(defaultTeamTranslations, overwriteTranslation)
47+
const { data, isError, isLoading } = useUserSeatsQuery()
48+
const userSeatUpdate = useUserSeatUpdateMutation()
49+
50+
const idMapping = (value: UserSeat): string => value.customerUUID + value.email
51+
52+
// TODO do input validation
53+
return (
54+
<Page pageTitle={titleWrapper(translation.team)} mainContainerClassName={tw('min-h-[auto] pb-6')}>
55+
<Section titleText={translation.team}>
56+
<LoadingAndErrorComponent isLoading={isLoading} hasError={isError} minimumLoadingDuration={200}>
57+
{!!data && (
58+
<div className={tw('flex flex-col gap-y-1')}>
59+
<span>{translation.teamDescription}</span>
60+
<Table
61+
data={data}
62+
identifierMapping={idMapping}
63+
header={[
64+
(<span key="user">{translation.user}</span>),
65+
(<span key="role">{translation.role}</span>),
66+
(<span key="enabled">{translation.enabled}</span>)
67+
]}
68+
rowMappingToCells={dataObject => [
69+
(
70+
<div key={idMapping(dataObject) + '-name'} className={tw('flex flex-col')}>
71+
<span
72+
className={tw('text-lg font-semibold')}>{`${dataObject.firstName} ${dataObject.lastName}`}</span>
73+
<span className={tw('text-gray-400')}>{dataObject.email}</span>
74+
</div>
75+
),
76+
(
77+
<Select<UserRole>
78+
key={idMapping(dataObject) + '-role'}
79+
value={dataObject.role}
80+
options={userRoles.map(role => ({ value: role, label: translation[role] }))}
81+
onChange={role => userSeatUpdate.mutate({ ...dataObject, role })}
82+
/>
83+
),
84+
(
85+
<Checkbox
86+
key={idMapping(dataObject) + '-enabled'}
87+
checked={dataObject.enabled}
88+
onChange={enabled => userSeatUpdate.mutate({ ...dataObject, enabled })}
89+
/>
90+
)
91+
]}
92+
/>
93+
</div>
94+
)}
95+
</LoadingAndErrorComponent>
96+
</Section>
97+
</Page>
98+
)
99+
}
100+
101+
export default Team

0 commit comments

Comments
 (0)