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

Commit e275c51

Browse files
committed
feat: update design
1 parent b558c57 commit e275c51

13 files changed

Lines changed: 317 additions & 145 deletions

File tree

customer/.env.development

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
NEXT_PUBLIC_API_URL=http://localhost:8000
2-
NEXT_PUBLIC_OIDC_PROVIDER=http://localhost:8080/realms/test
3-
NEXT_PUBLIC_CLIENT_ID=customer-api-keycloak
1+
NEXT_PUBLIC_API_URL=https://api.customer.helpwave.de
2+
NEXT_PUBLIC_OIDC_PROVIDER=https://id.helpwave.de/realms/main
3+
NEXT_PUBLIC_CLIENT_ID=customer.helpwave.de
44
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/auth/callback
55
NEXT_PUBLIC_POST_LOGOUT_REDIRECT_URI=http://localhost:3000/
66
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51QqZfARtChvireAMzq4687T4m4TpyJvADX6IyM8A9u47lCUMMi0e2SNfxoSIDMM9DebiQCYwBa7mZRkJ50uZZjGC00hYO7FzL4

customer/.env.production

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
NEXT_PUBLIC_API_URL=https://api.customer.helpwave.de
22
NEXT_PUBLIC_OIDC_PROVIDER=https://id.helpwave.de/realms/main
3-
NEXT_PUBLIC_CLIENT_ID=customer
3+
NEXT_PUBLIC_CLIENT_ID=customer.helpwave.de
44
NEXT_PUBLIC_REDIRECT_URI=https://issue-1206-customer-api-intg.customer-3de.pages.dev/auth/callback
55
NEXT_PUBLIC_POST_LOGOUT_REDIRECT_URI=https://issue-1206-customer-api-intg.customer-3de.pages.dev/
66
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51QqZfARtChvireAMzq4687T4m4TpyJvADX6IyM8A9u47lCUMMi0e2SNfxoSIDMM9DebiQCYwBa7mZRkJ50uZZjGC00hYO7FzL4

customer/api/dataclasses/customer_product.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
11
import type { Product, ProductPlan } from '@/api/dataclasses/product'
22
import { ProductHelpers } from '@/api/dataclasses/product'
3+
import type { Translation } from '@helpwave/common/hooks/useTranslation'
4+
5+
export type CustomerProductStatus =
6+
'trialing'
7+
| 'active'
8+
| 'activation'
9+
| 'payment'
10+
| 'scheduled'
11+
| 'canceled'
12+
| 'expired'
13+
| 'refunded'
14+
15+
export type CustomerProductStatusTranslation = Record<CustomerProductStatus, string>
16+
17+
export const defaultCustomerProductStatusTranslation : Translation<CustomerProductStatusTranslation> = {
18+
en: {
19+
trialing: 'Trialing',
20+
active: 'Active',
21+
activation: 'Activation Required',
22+
payment: 'Awaiting Payment',
23+
scheduled: 'Scheduled Cancelation',
24+
canceled: 'Canceled',
25+
expired: 'Expired',
26+
refunded: 'Refunded'
27+
},
28+
de: {
29+
trialing: 'Testphase',
30+
active: 'Aktiv',
31+
activation: 'Aktivierung erforderlich',
32+
payment: 'Zahlung ausstehend',
33+
scheduled: 'Geplante Kündingung',
34+
canceled: 'Kündigung',
35+
expired: 'Abgelaufen',
36+
refunded: 'Erstattet'
37+
}
38+
}
339

440
export interface CustomerProduct {
541
/** The identifier of the booking
@@ -13,6 +49,8 @@ export interface CustomerProduct {
1349
productUUID: string,
1450
/** The identifier of the plan for the booked product */
1551
productPlanUUID: string,
52+
/** The status of the booking */
53+
status: CustomerProductStatus,
1654
/** The number of seats allocated */
1755
seats?: number,
1856
/** The date from which the booking starts */
@@ -44,6 +82,8 @@ export interface ResolvedCustomerProduct {
4482
uuid: string,
4583
/** The identifier of the customer that booked the product */
4684
customerUUID: string,
85+
/** The status of the booking */
86+
status: CustomerProductStatus,
4787
/** The number of seats allocated */
4888
seats?: number,
4989
/** The date from which the booking starts */
@@ -67,6 +107,7 @@ function fromJson(json: any): CustomerProduct {
67107
customerUUID: json.customer_uuid,
68108
productUUID: json.product_uuid,
69109
productPlanUUID: json.product_plan_uuid,
110+
status: json.status,
70111
seats: json.seats,
71112
startDate: new Date(json.start_date),
72113
nextPaymentDate: json.next_payment_date ? new Date(json.next_payment_date) : undefined,
@@ -82,6 +123,7 @@ function fromJsonResolvedCustomerProduct(json: any): ResolvedCustomerProduct {
82123
return {
83124
uuid: json.uuid,
84125
customerUUID: json.customer_uuid,
126+
status: json.status,
85127
seats: json.seats,
86128
startDate: new Date(json.start_date),
87129
nextPaymentDate: json.next_payment_date ? new Date(json.next_payment_date) : undefined,
@@ -101,6 +143,7 @@ function toJson(customerProduct: CustomerProduct): any {
101143
customer_uuid: customerProduct.customerUUID,
102144
product_uuid: customerProduct.productUUID,
103145
product_plan_uuid: customerProduct.productPlanUUID,
146+
status: customerProduct.status,
104147
seats: customerProduct.seats,
105148
start_date: customerProduct.startDate.toISOString(),
106149
next_payment_date: customerProduct.nextPaymentDate?.toISOString(),
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type { Languages } from '@helpwave/common/hooks/useLanguage'
2+
import type { PropsForTranslation } from '@helpwave/common/hooks/useTranslation'
3+
import { useTranslation } from '@helpwave/common/hooks/useTranslation'
4+
import { useContractsForProductsQuery } from '@/api/mutations/contract_mutations'
5+
import { LoadingAndErrorComponent } from '@helpwave/common/components/LoadingAndErrorComponent'
6+
import Link from 'next/link'
7+
import { ExternalLink } from 'lucide-react'
8+
import { tw } from '@twind/core'
9+
10+
type ContractListTranslation = {
11+
contracts: string,
12+
noContracts: string,
13+
show: string,
14+
}
15+
16+
const defaultContractListTranslations: Record<Languages, ContractListTranslation> = {
17+
en: {
18+
contracts: 'Contracts',
19+
noContracts: 'No Contracts',
20+
show: 'Show'
21+
},
22+
de: {
23+
contracts: 'Verträge',
24+
noContracts: 'Keine Verträge',
25+
show: 'Anzeigen'
26+
}
27+
}
28+
29+
export type ContractListProps = {
30+
productIds: string[],
31+
}
32+
33+
export const ContractList = ({
34+
productIds,
35+
overwriteTranslation
36+
}: PropsForTranslation<ContractListTranslation, ContractListProps>) => {
37+
const translation = useTranslation(defaultContractListTranslations, overwriteTranslation)
38+
const {
39+
data: contracts,
40+
isLoading,
41+
isError,
42+
} = useContractsForProductsQuery(productIds)
43+
44+
return (
45+
<LoadingAndErrorComponent isLoading={isLoading} hasError={isError}>
46+
{contracts && (
47+
<div className={tw('flex flex-col gap-y-1')}>
48+
<h3 className={tw('text-lg font-semibold')}>{translation.contracts}</h3>
49+
{contracts.length === 0 ? (
50+
<span className={tw('text-bg-gray-300')}>{translation.noContracts}</span>
51+
) : (
52+
contracts.map(contract => (
53+
<Link
54+
href={contract.url}
55+
target="_blank"
56+
key={contract.uuid}
57+
className={tw('inline-flex flex-row gap-x-2')}
58+
>
59+
{contract.key}
60+
<span className={tw('inline-flex flex-row items-center')}>
61+
(
62+
<div className={tw('inline-flex flex-row items-center gap-x-0.5')}>
63+
{`${translation.show}`}
64+
<ExternalLink size={16}/>
65+
</div>
66+
)
67+
</span>
68+
</Link>
69+
))
70+
)}
71+
</div>
72+
)}
73+
</LoadingAndErrorComponent>
74+
)
75+
}

customer/components/layout/MobileNavigationOverlay.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const MobileNavigationOverlay = ({ items, onCloseClick = noop, className
6767
<Link
6868
href={item.url}
6969
key={i}
70+
target={item.isExternal ?? false ? '_blank' : undefined}
7071
className={tx(
7172
'flex flex-row justify-between items-center px-4 py-2 hover:bg-hw-primary-500/40 w-full text-lg font-semibold rounded-md',
7273
{ 'bg-gray-100': router.pathname === item.url, 'bg-white': router.pathname !== item.url }

customer/components/layout/NavigationSidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type NavItem = {
1919
name: Record<Languages, string>,
2020
icon?: ReactNode,
2121
url: string,
22+
isExternal?: boolean,
2223
subItems?: NavItem[],
2324
}
2425

@@ -67,6 +68,7 @@ export const NavigationSidebar = ({ items, className }: NavSidebarProps) => {
6768
<Link
6869
href={item.url}
6970
key={i}
71+
target={item.isExternal ?? false ? '_blank' : undefined}
7072
className={tx(
7173
'px-4 py-2 bg-gray-50 hover:bg-hw-primary-500/40 flex flex-row gap-x-2 px-4 items-center',
7274
{ 'bg-gray-200': router.pathname == item.url }

customer/components/layout/Page.tsx

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

@@ -39,6 +39,18 @@ const navItems: NavItem[] = [
3939
{ name: { en: 'Products', de: 'Produkte' }, url: '/products', icon: (<Package size={24}/>) },
4040
{ name: { en: 'Invoices', de: 'Rechnungen' }, url: '/invoices', icon: (<Receipt size={24}/>) },
4141
{ name: { en: 'Settings', de: 'Einstellungen' }, url: '/settings', icon: (<Settings size={24}/>) },
42+
{
43+
name: { en: 'Imprint', de: 'Impressum' },
44+
url: 'https://cdn.helpwave.de/imprint.html',
45+
isExternal: true,
46+
icon: (<Section size={24}/>)
47+
},
48+
{
49+
name: { en: 'Support', de: 'Support' },
50+
url: 'mailto:contact@helpwave.de',
51+
isExternal: true,
52+
icon: (<MessageCircleQuestionIcon size={24}/>)
53+
},
4254
]
4355

4456
/**

customer/pages/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { withOrganization } from '@/hooks/useOrganization'
77
import { withAuth } from '@/hooks/useAuth'
88
import { useRouter } from 'next/router'
99
import { useEffect } from 'react'
10+
import { tw } from '@twind/core'
11+
import { LoadingAnimation } from '@helpwave/common/components/LoadingAnimation'
1012

1113
type DashboardTranslation = {
1214
dashboard: string,
@@ -30,12 +32,14 @@ const Dashboard: NextPage<PropsForTranslation<DashboardTranslation, DashboardSer
3032
const router = useRouter()
3133

3234
useEffect(() => {
33-
router.push('/products').catch(console.error)
35+
router.push('/products').catch(console.error)
3436
}, [router])
3537

3638
return (
37-
<Page pageTitle={titleWrapper(translation.dashboard)}>
38-
<div>This is the {translation.dashboard} page</div>
39+
<Page pageTitle={titleWrapper(translation.dashboard)} mainContainerClassName={tw('h-full')}>
40+
<div className={tw('flex flex-col h-full items-center justify-center')}>
41+
{<LoadingAnimation/>}
42+
</div>
3943
</Page>
4044
)
4145
}

0 commit comments

Comments
 (0)