MED-137: add doctor other jobs view (#55)

* add doctor jobs view

* change translation

* another translation change

* clean up

* add analaysis detail view to paths config

* translation

* merge fix

* fix path

* move components to shared

* refactor

* imports

* clean up
This commit is contained in:
Helena
2025-08-25 11:12:57 +03:00
committed by GitHub
parent ee86bb8829
commit 195af1db3d
156 changed files with 2823 additions and 364 deletions

View File

@@ -2,6 +2,7 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
export async function generateMetadata() {
const { t } = await createI18nServerInstance();

View File

@@ -2,6 +2,7 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
export async function generateMetadata() {
const { t } = await createI18nServerInstance();

View File

@@ -1,8 +1,8 @@
import { Footer } from '@kit/ui/marketing';
import { Trans } from '@kit/ui/trans';
import { AppLogo } from '~/components/app-logo';
import appConfig from '~/config/app.config';
import { AppLogo } from '@kit/shared/components/app-logo';
import appConfig from '@kit/shared/config/app.config';
export function SiteFooter() {
return (

View File

@@ -13,8 +13,10 @@ import { Button } from '@kit/ui/button';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import featuresFlagConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { featureFlagsConfig } from '@kit/shared/config';
import { pathsConfig } from '@kit/shared/config';
const ModeToggle = dynamic(() =>
import('@kit/ui/mode-toggle').then((mod) => ({
@@ -30,7 +32,7 @@ const paths = {
};
const features = {
enableThemeToggle: featuresFlagConfig.enableThemeToggle,
enableThemeToggle: featureFlagsConfig.enableThemeToggle,
};
export function SiteHeaderAccountSection({

View File

@@ -2,7 +2,7 @@ import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace
import { Header } from '@kit/ui/marketing';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
import { SiteHeaderAccountSection } from './site-header-account-section';

View File

@@ -8,6 +8,7 @@ import { createCmsClient } from '@kit/cms';
import { withI18n } from '~/lib/i18n/with-i18n';
import { Post } from '../../blog/_components/post';
interface BlogPageProps {

View File

@@ -6,6 +6,7 @@ import { ContactForm } from '~/(marketing)/contact/_components/contact-form';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
export async function generateMetadata() {
const { t } = await createI18nServerInstance();

View File

@@ -9,6 +9,7 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
export const generateMetadata = async () => {
const { t } = await createI18nServerInstance();

View File

@@ -4,7 +4,7 @@ import { SiteFooter } from '~/(marketing)/_components/site-footer';
import { SiteHeader } from '~/(marketing)/_components/site-header';
import { withI18n } from '~/lib/i18n/with-i18n';
import { loadCurrentUserAccounts } from '../home/(user)/_lib/server/load-accounts';
import { loadCurrentUserAccounts } from '~/home/(user)/_lib/server/load-accounts';
function SiteLayout(props: React.PropsWithChildren) {
const accounts = use(loadCurrentUserAccounts());

View File

@@ -1,6 +1,6 @@
import Link from 'next/link';
import { MedReportLogo } from '@/components/med-report-logo';
import { MedReportLogo } from '@kit/shared/components/med-report-logo';
import { ArrowRightIcon } from 'lucide-react';
import { CtaButton, Hero } from '@kit/ui/marketing';

View File

@@ -4,7 +4,7 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { SubmitButton } from '@/components/ui/submit-button';
import { SubmitButton } from '@kit/shared/components/ui/submit-button';
import { sendCompanyOfferEmail } from '@/lib/services/mailer.service';
import { CompanySubmitData } from '@/lib/types/company';
import { companyOfferSchema } from '@/lib/validations/company-offer.schema';

View File

@@ -1,8 +1,7 @@
import React from 'react';
import { MedReportLogo } from '@/components/med-report-logo';
import { withI18n } from '@/lib/i18n/with-i18n';
import { MedReportLogo } from '@kit/shared/components/med-report-logo';
import { withI18n } from '~/lib/i18n/with-i18n';
import { Trans } from '@kit/ui/trans';
import CompanyOfferForm from './_components/company-offer-form';

View File

@@ -1,5 +1,6 @@
import { withI18n } from '~/lib/i18n/with-i18n';
function SiteLayout(props: React.PropsWithChildren) {
return (
<div className={'flex min-h-[100vh] flex-col justify-center items-center'}>

View File

@@ -19,8 +19,8 @@ import {
useSidebar,
} from '@kit/ui/shadcn-sidebar';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { AppLogo } from '@kit/shared/components/app-logo';
import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container';
export function AdminSidebar({
accounts,

View File

@@ -4,7 +4,7 @@ import { enhanceRouteHandler } from '@kit/next/routes';
import { getLogger } from '@kit/shared/logger';
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
import billingConfig from '~/config/billing.config';
import { billingConfig } from '@kit/shared/config';
/**
* @description Handle the webhooks from Stripe related to checkouts

View File

@@ -7,9 +7,11 @@ import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
import { Button } from '@kit/ui/button';
import { Trans } from '@kit/ui/trans';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { withI18n } from '~/lib/i18n/with-i18n';
interface AuthCallbackErrorPageProps {
searchParams: Promise<{
error: string;

View File

@@ -1,6 +1,6 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
function AuthLayout({ children }: React.PropsWithChildren) {
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;

View File

@@ -4,7 +4,8 @@ import type { NextRequest } from 'next/server';
import { createAuthCallbackService } from '@kit/supabase/auth';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
export async function GET(request: NextRequest) {
const service = createAuthCallbackService(getSupabaseServerClient());

View File

@@ -1,6 +1,6 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
function AuthLayout({ children }: React.PropsWithChildren) {
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;

View File

@@ -3,7 +3,8 @@ import { NextRequest, NextResponse } from 'next/server';
import { createAuthCallbackService } from '@kit/supabase/auth';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
export async function GET(request: NextRequest) {
const service = createAuthCallbackService(getSupabaseServerClient());

View File

@@ -2,7 +2,7 @@
import React from 'react';
import pathsConfig from '@/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { useTranslation } from 'react-i18next';
import { usePersonalAccountData } from '@kit/accounts/hooks/use-personal-account-data';

View File

@@ -1,5 +1,6 @@
import { withI18n } from '~/lib/i18n/with-i18n';
async function SiteLayout(props: React.PropsWithChildren) {
return (
<div className={'flex min-h-[100vh] flex-col items-center justify-center'}>

View File

@@ -1,7 +1,6 @@
import { redirect } from 'next/navigation';
import pathsConfig from '@/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { withI18n } from '~/lib/i18n/with-i18n';

View File

@@ -1,6 +1,6 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
function AuthLayout({ children }: React.PropsWithChildren) {
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;

View File

@@ -5,10 +5,12 @@ import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
export const generateMetadata = async () => {
const { t } = await createI18nServerInstance();

View File

@@ -1,6 +1,6 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
function AuthLayout({ children }: React.PropsWithChildren) {
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;

View File

@@ -1,14 +1,12 @@
import Link from 'next/link';
import { register } from 'module';
import { SignInMethodsContainer } from '@kit/auth/sign-in';
import { authConfig, pathsConfig } from '@kit/shared/config';
import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import authConfig from '~/config/auth.config';
import pathsConfig from '~/config/paths.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';

View File

@@ -1,6 +1,6 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
function AuthLayout({ children }: React.PropsWithChildren) {
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;

View File

@@ -1,13 +1,13 @@
import Link from 'next/link';
import { SignUpMethodsContainer } from '@kit/auth/sign-up';
import { authConfig, pathsConfig } from '@kit/shared/config';
import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import authConfig from '~/config/auth.config';
import pathsConfig from '~/config/paths.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
export const generateMetadata = async () => {

View File

@@ -8,7 +8,8 @@ import { AccountSubmitData, createAuthApi } from '@kit/auth/api';
import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { UpdateAccountSchema } from '../schemas/update-account.schema';

View File

@@ -1,5 +1,6 @@
import { withI18n } from '~/lib/i18n/with-i18n';
async function SiteLayout(props: React.PropsWithChildren) {
return (
<div className={'flex min-h-[100vh] flex-col items-center justify-center'}>

View File

@@ -1,11 +1,11 @@
import { redirect } from 'next/navigation';
import { BackButton } from '@/components/back-button';
import { MedReportLogo } from '@/components/med-report-logo';
import pathsConfig from '@/config/paths.config';
import { signOutAction } from '@/lib/actions/sign-out';
import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client';
import { BackButton } from '@kit/shared/components/back-button';
import { MedReportLogo } from '@kit/shared/components/med-report-logo';
import { pathsConfig } from '@kit/shared/config';
import { Trans } from '@kit/ui/trans';
import { withI18n } from '~/lib/i18n/with-i18n';

View File

@@ -1,6 +1,6 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { AppLogo } from '@kit/shared/components/app-logo';
function AuthLayout({ children }: React.PropsWithChildren) {
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;

View File

@@ -4,7 +4,8 @@ import { MultiFactorChallengeContainer } from '@kit/auth/mfa';
import { checkRequiresMultiFactorAuthentication } from '@kit/supabase/check-requires-mfa';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';

View File

@@ -0,0 +1,242 @@
'use client';
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useQueryClient } from '@tanstack/react-query';
import { useForm } from 'react-hook-form';
import { giveFeedbackAction } from '@kit/doctor/actions/doctor-server-actions';
import {
getDOBWithAgeStringFromPersonalCode,
getResultSetName,
} from '@kit/doctor/lib/helpers';
import {
AnalysisResponse,
DoctorFeedback,
Order,
Patient,
} from '@kit/doctor/schema/doctor-analysis-detail-view.schema';
import {
DoctorAnalysisFeedbackForm,
doctorAnalysisFeedbackSchema,
} from '@kit/doctor/schema/doctor-analysis.schema';
import ConfirmationModal from '@kit/shared/components/confirmation-modal';
import { getFullName } from '@kit/shared/utils';
import { useUser } from '@kit/supabase/hooks/use-user';
import { Button } from '@kit/ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from '@kit/ui/form';
import { toast } from '@kit/ui/sonner';
import { Textarea } from '@kit/ui/textarea';
import { Trans } from '@kit/ui/trans';
import Analysis from '~/home/(user)/(dashboard)/analysis-results/_components/analysis';
import { bmiFromMetric } from '~/lib/utils';
export default function AnalysisView({
patient,
order,
analyses,
feedback,
}: {
patient: Patient;
order: Order;
analyses: AnalysisResponse[];
feedback?: DoctorFeedback;
}) {
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
const { data: user } = useUser();
const isInProgress =
!!feedback?.status &&
feedback?.doctor_user_id &&
feedback?.status !== 'COMPLETED';
const isReadOnly =
!isInProgress ||
(!!feedback?.doctor_user_id && feedback?.doctor_user_id !== user?.id);
const form = useForm({
resolver: zodResolver(doctorAnalysisFeedbackSchema),
defaultValues: {
feedbackValue: feedback?.value ?? '',
userId: patient.userId,
},
});
const queryClient = useQueryClient();
if (!patient || !order || !analyses) {
return null;
}
const onSubmit = async (
data: DoctorAnalysisFeedbackForm,
status: 'DRAFT' | 'COMPLETED',
) => {
try {
const feedbackPromise = giveFeedbackAction({
...data,
analysisOrderId: order.analysisOrderId,
status,
});
toast.promise(() => feedbackPromise, {
success: <Trans i18nKey={'doctor:updateFeedbackSuccess'} />,
error: <Trans i18nKey={'doctor:updateFeedbackError'} />,
loading: <Trans i18nKey={'doctor:updateFeedbackLoading'} />,
});
queryClient.invalidateQueries({
predicate: (query) => query.queryKey.includes('doctor-jobs'),
});
return setIsConfirmOpen(false);
} catch (error) {
toast.error(<Trans i18nKey="common:genericServerError" />);
}
};
const handleDraftSubmit = () => {
const formData = form.getValues();
onSubmit(formData, 'DRAFT');
};
const handleCompleteSubmit = () => {
setIsConfirmOpen(true);
};
const confirmComplete = () => {
const formData = form.getValues();
onSubmit(formData, 'COMPLETED');
};
return (
<>
<h3>
<Trans
i18nKey={getResultSetName(
order.title,
order.isPackage,
Object.keys(analyses)?.length,
)}
/>
</h3>
<div className="grid grid-cols-2">
<div className="font-bold">
<Trans i18nKey="doctor:name" />
</div>
<div>{getFullName(patient.firstName, patient.lastName)}</div>
<div className="font-bold">
<Trans i18nKey="doctor:personalCode" />
</div>
<div>{patient.personalCode ?? ''}</div>
<div className="font-bold">
<Trans i18nKey="doctor:dobAndAge" />
</div>
<div>{getDOBWithAgeStringFromPersonalCode(patient.personalCode)}</div>
<div className="font-bold">
<Trans i18nKey="doctor:height" />
</div>
<div>{patient.height}</div>
<div className="font-bold">
<Trans i18nKey="doctor:weight" />
</div>
<div>{patient.weight}</div>
<div className="font-bold">
<Trans i18nKey="doctor:bmi" />
</div>
<div>{bmiFromMetric(patient?.height ?? 0, patient?.weight ?? 0)}</div>
<div className="font-bold">
<Trans i18nKey="doctor:smoking" />
</div>
<div></div>
<div className="font-bold">
<Trans i18nKey="doctor:phone" />
</div>
<div>{patient.phone}</div>
<div className="font-bold">
<Trans i18nKey="doctor:email" />
</div>
<div>{patient.email}</div>
</div>
<h3>
<Trans i18nKey="doctor:results" />
</h3>
<div className="flex flex-col gap-2">
{analyses.map((analysisData) => {
return (
<Analysis
key={analysisData.id}
analysisElement={{
analysis_name_lab: analysisData.analysis_name,
}}
results={analysisData}
/>
);
})}
</div>
<h3>
<Trans i18nKey="doctor:feedback" />
</h3>
<p>{feedback?.value ?? '-'}</p>
{!isReadOnly && (
<Form {...form}>
<form className="space-y-4 lg:w-1/2">
<FormField
control={form.control}
name="feedbackValue"
render={({ field }) => (
<FormItem>
<FormControl>
<Textarea {...field} disabled={isReadOnly} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex gap-2">
<Button
variant="outline"
onClick={(e) => {
e.preventDefault();
handleDraftSubmit();
}}
disabled={isReadOnly}
>
<Trans i18nKey="common:saveAsDraft" />
</Button>
<Button
onClick={(e) => {
e.preventDefault();
handleCompleteSubmit();
}}
disabled={isReadOnly}
>
<Trans i18nKey="common:save" />
</Button>
</div>
</form>
</Form>
)}
<ConfirmationModal
isOpen={isConfirmOpen}
onClose={() => setIsConfirmOpen(false)}
onConfirm={confirmComplete}
titleKey="doctor:confirmFeedbackModal.title"
descriptionKey="doctor:confirmFeedbackModal.description"
/>
</>
);
}

View File

@@ -0,0 +1,36 @@
'use client';
import {
getOpenResponsesAction,
getOtherResponsesAction,
getUserDoneResponsesAction,
getUserInProgressResponsesAction,
} from '@kit/doctor/actions/table-data-fetching-actions';
import ResultsTableWrapper from './results-table-wrapper';
export default function Dashboard() {
return (
<>
<ResultsTableWrapper
titleKey="doctor:openReviews"
action={getOpenResponsesAction}
queryKey="doctor-open-jobs"
/>
<ResultsTableWrapper
titleKey="doctor:myReviews"
action={getUserInProgressResponsesAction}
queryKey="doctor-in-progress-jobs"
/>
<ResultsTableWrapper
titleKey="doctor:completedReviews"
action={getUserDoneResponsesAction}
queryKey="doctor-done-jobs"
/>
<ResultsTableWrapper
titleKey="doctor:otherReviews"
action={getOtherResponsesAction}
queryKey="doctor-other-jobs"
/>
</>
);
}

View File

@@ -1,7 +1,7 @@
import { notFound } from 'next/navigation';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { isDoctor } from '../lib/server/utils/is-doctor';
import { isDoctor } from '@kit/doctor/lib/server/utils/is-doctor';
type LayoutOrPageComponent<Params> = React.ComponentType<Params>;

View File

@@ -20,8 +20,10 @@ import {
} from '@kit/ui/shadcn-sidebar';
import { Trans } from '@kit/ui/trans';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { AppLogo } from '@kit/shared/components/app-logo';
import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container';
import { pathsConfig } from '@kit/shared/config';
export function DoctorSidebar({
accounts,
@@ -33,7 +35,11 @@ export function DoctorSidebar({
return (
<Sidebar collapsible="icon">
<SidebarHeader className={'m-2'}>
<AppLogo href={'/doctor'} className="max-w-full" compact={!open} />
<AppLogo
href={pathsConfig.app.doctor}
className="max-w-full"
compact={!open}
/>
</SidebarHeader>
<SidebarContent>
@@ -44,10 +50,49 @@ export function DoctorSidebar({
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuButton isActive={path === '/doctor'} asChild>
<Link className={'flex gap-2.5'} href={'/doctor'}>
<SidebarMenuButton
isActive={path === pathsConfig.app.doctor}
asChild
>
<Link className={'flex gap-2.5'} href={pathsConfig.app.doctor}>
<LayoutDashboard className={'h-4'} />
<span>Dashboard</span>
<Trans i18nKey={'doctor:sidebar.dashboard'} />
</Link>
</SidebarMenuButton>
<SidebarMenuButton
isActive={path === pathsConfig.app.openJobs}
asChild
>
<Link
className={'flex gap-2.5'}
href={pathsConfig.app.openJobs}
>
<LayoutDashboard className={'h-4'} />
<Trans i18nKey={'doctor:sidebar.openReviews'} />
</Link>
</SidebarMenuButton>
<SidebarMenuButton
isActive={path === pathsConfig.app.myJobs}
asChild
>
<Link
className={'flex gap-2.5'}
href={pathsConfig.app.myJobs}
>
<LayoutDashboard className={'h-4'} />
<Trans i18nKey={'doctor:sidebar.myReviews'} />
</Link>
</SidebarMenuButton>
<SidebarMenuButton
isActive={path === pathsConfig.app.completedJobs}
asChild
>
<Link
className={'flex gap-2.5'}
href={pathsConfig.app.completedJobs}
>
<LayoutDashboard className={'h-4'} />
<Trans i18nKey={'doctor:sidebar.completedReviews'} />
</Link>
</SidebarMenuButton>
</SidebarMenu>

View File

@@ -2,13 +2,14 @@ import Link from 'next/link';
import { Menu } from 'lucide-react';
import { pathsConfig } from '@kit/shared/config';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@kit/ui/dropdown-menu';
import pathsConfig from '../../../config/paths.config';
import { Trans } from '@kit/ui/trans';
export function DoctorMobileNavigation() {
return (
@@ -19,9 +20,30 @@ export function DoctorMobileNavigation() {
<DropdownMenuContent>
<DropdownMenuItem>
<Link href={pathsConfig.app.doctor}>Home</Link>
<Link href={pathsConfig.app.home}>
<Trans i18nKey={'common:routes.home'} />
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link href={pathsConfig.app.doctor}>
<Trans i18nKey={'doctor:sidebar.dashboard'} />
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link href={pathsConfig.app.openJobs}>
<Trans i18nKey={'doctor:sidebar.openReviews'} />
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link href={pathsConfig.app.myJobs}>
<Trans i18nKey={'doctor:sidebar.myReviews'} />
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link href={pathsConfig.app.completedJobs}>
<Trans i18nKey={'doctor:sidebar.completedReviews'} />
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);

View File

@@ -0,0 +1,120 @@
'use client';
import { useState } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import {
PaginatedData,
ResponseTable,
ServerActionResponse,
} from '@kit/doctor/schema/doctor-analysis.schema';
import TableSkeleton from '@kit/shared/components/table-skeleton';
import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils';
import ResultsTable from './results-table';
const PAGE_SIZE = 10;
export default function ResultsTableWrapper({
titleKey,
action,
queryKey,
}: {
titleKey: string;
action: ({
page,
pageSize,
}: {
page: number;
pageSize: number;
}) => Promise<ServerActionResponse<PaginatedData<ResponseTable>>>;
queryKey: string;
}) {
const [page, setPage] = useState(1);
const {
data: jobs,
isLoading,
isError,
isFetching,
} = useQuery({
queryKey: [queryKey, 'doctor-jobs', page],
queryFn: async () => await action({ page: page, pageSize: PAGE_SIZE }),
placeholderData: (previousData) => previousData,
});
const queryClient = useQueryClient();
const onJobUpdate = () => {
queryClient.invalidateQueries({
predicate: (query) => query.queryKey.includes('doctor-jobs'),
});
};
const createPageChangeHandler = (setPage: (page: number) => void) => {
return async ({ page }: { page: number; pageSize: number }) => {
setPage(page);
return { success: true, data: null };
};
};
if (isLoading) {
return (
<>
<h3>
<Trans i18nKey={titleKey} />
</h3>
<div className="relative">
<div
className={`transition-opacity duration-200 ${
isFetching ? 'opacity-50' : 'opacity-100'
}`}
>
<TableSkeleton />
</div>
</div>
</>
);
}
if (isError) {
return (
<>
<h3>
<Trans i18nKey={titleKey} />
</h3>
<div className="flex items-center justify-center p-8">
<div className="text-lg text-red-600">
<Trans i18nKey="common:genericServerError" />
</div>
</div>
</>
);
}
return (
<>
<h3>
<Trans i18nKey={titleKey} />
</h3>
<div className="relative">
<div
className={cn('opacity-100 transition-opacity duration-200', {
'opacity-50': isFetching,
})}
>
<ResultsTable
results={jobs?.data?.data || []}
pagination={jobs?.data?.pagination}
fetchAction={createPageChangeHandler(setPage)}
onJobUpdate={onJobUpdate}
/>
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,322 @@
'use client';
import { useTransition } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { format } from 'date-fns';
import { Eye, LoaderCircle } from 'lucide-react';
import {
selectJobAction,
unselectJobAction,
} from '@kit/doctor/actions/doctor-server-actions';
import { getResultSetName } from '@kit/doctor/lib/helpers';
import { ResponseTable } from '@kit/doctor/schema/doctor-analysis.schema';
import { pathsConfig } from '@kit/shared/config';
import { getFullName } from '@kit/shared/utils';
import { useUser } from '@kit/supabase/hooks/use-user';
import { Button } from '@kit/ui/button';
import { toast } from '@kit/ui/sonner';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@kit/ui/table';
import { Trans } from '@kit/ui/trans';
function DoctorCell({
doctorUserId,
doctorName,
analysisOrderId,
userId,
isRemovable,
onJobUpdate,
linkTo,
}: {
doctorUserId?: string;
doctorName?: string;
analysisOrderId: number;
userId: string;
isRemovable?: boolean;
linkTo: string;
onJobUpdate: () => void;
}) {
const [isPending, startTransition] = useTransition();
const router = useRouter();
const handleSelectJob = () => {
startTransition(async () => {
const result = await selectJobAction({
analysisOrderId,
userId,
});
if (result?.success) {
onJobUpdate();
router.push(linkTo);
} else {
toast.error('common.genericServerError');
}
});
};
const handleUnselectJob = () => {
startTransition(async () => {
const result = await unselectJobAction({
analysisOrderId,
});
if (result?.success) {
onJobUpdate();
} else {
toast.error('common.genericServerError');
}
});
};
if (isRemovable) {
return (
<Button
className="w-16"
size="sm"
variant="destructive"
onClick={handleUnselectJob}
disabled={isPending}
>
{isPending ? (
<LoaderCircle className="h-4 w-4 animate-spin" />
) : (
<Trans i18nKey="doctor:unselectJob" />
)}
</Button>
);
}
if (!doctorUserId) {
return (
<Button
className="w-16"
size="sm"
onClick={handleSelectJob}
disabled={isPending}
>
{isPending ? (
<LoaderCircle className="h-4 w-4 animate-spin" />
) : (
<Trans i18nKey="doctor:selectJob" />
)}
</Button>
);
}
return <>{doctorName}</>;
}
export default function ResultsTable({
results = [],
pagination = {
currentPage: 1,
totalPages: 1,
totalCount: 0,
pageSize: 10,
},
fetchAction,
onJobUpdate,
}: {
results: ResponseTable[] | null;
pagination?: {
currentPage: number;
totalPages: number;
totalCount: number;
pageSize: number;
};
fetchAction: ({
page,
pageSize,
}: {
page: number;
pageSize: number;
}) => Promise<{
success: boolean;
data: null;
}>;
onJobUpdate: () => void;
}) {
const [isPending, startTransition] = useTransition();
const { data: currentUser } = useUser();
const fetchPage = async (page: number) => {
startTransition(async () => {
const result = await fetchAction({
page,
pageSize: pagination.pageSize,
});
if (!result.success) {
toast.error('common.genericServerError');
}
});
};
const handleNextPage = () => {
if (pagination.currentPage < pagination.totalPages) {
fetchPage(pagination.currentPage + 1);
}
};
const handlePrevPage = () => {
if (pagination.currentPage > 1) {
fetchPage(pagination.currentPage - 1);
}
};
const handleJobUpdate = () => {
onJobUpdate();
fetchPage(pagination.currentPage);
};
if (!results?.length) {
return (
<p>
<Trans i18nKey="common:noData" />.
</p>
);
}
return (
<>
<Table className="border-separate rounded-lg border">
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
<TableRow>
<TableHead className="w-6"></TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.patientName" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.serviceName" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.orderNr" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.time" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.resultsStatus" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.assignedTo" />
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{results
?.sort((a, b) =>
(a.created_at ?? '') > (b.created_at ?? '') ? -1 : 1,
)
.map((result) => {
const isCompleted = result.feedback?.status === 'COMPLETED';
const isCurrentDoctorJob =
!!result.doctor?.primary_owner_user_id &&
result.doctor?.primary_owner_user_id === currentUser?.id;
const resultsReceived = result.elements.length || 0;
const elementsInOrder = Array.from(
new Set(result.analysis_order_id.analysis_element_ids),
)?.length;
return (
<TableRow key={result.order_number}>
<TableCell className="text-center">
<Link
href={`/${pathsConfig.app.analysisDetails}/${result.id}`}
>
<Eye />
</Link>
</TableCell>
<TableCell>
{getFullName(
result.patient?.name,
result.patient?.last_name,
)}
</TableCell>
<TableCell>
<Trans
i18nKey={getResultSetName(
result.order?.title ?? '-',
result.order?.isPackage,
result.elements?.length || 0,
)}
/>
</TableCell>
<TableCell>{result.order_number}</TableCell>
<TableCell>
{result.firstSampleGivenAt
? format(result.firstSampleGivenAt, 'dd.MM.yyyy HH:mm')
: '-'}
</TableCell>
<TableCell>
<Trans
i18nKey={
resultsReceived === elementsInOrder
? 'doctor:resultsTable.responsesReceived'
: 'doctor:resultsTable.waitingForNr'
}
values={{
nr: elementsInOrder - resultsReceived,
}}
/>
</TableCell>
<TableCell>
<DoctorCell
doctorUserId={result.doctor?.primary_owner_user_id}
doctorName={getFullName(
result.doctor?.name,
result.doctor?.last_name,
)}
analysisOrderId={result.analysis_order_id?.id}
userId={result.patient?.id}
isRemovable={!isCompleted && isCurrentDoctorJob}
onJobUpdate={handleJobUpdate}
linkTo={`/${pathsConfig.app.analysisDetails}/${result.id}`}
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
<div className="mt-4 flex items-center justify-between">
<Button
onClick={handlePrevPage}
disabled={pagination.currentPage === 1 || isPending}
variant="outline"
>
<Trans i18nKey="common:previous" />
</Button>
<span className="text-sm text-gray-600">
<Trans
i18nKey={'common:pageOfPages'}
values={{
page: pagination.currentPage,
total: pagination.totalPages,
}}
/>
</span>
<Button
onClick={handleNextPage}
disabled={
pagination.currentPage === pagination.totalPages || isPending
}
variant="outline"
>
<Trans i18nKey="common:next" />
</Button>
</div>
</>
);
}

View File

@@ -0,0 +1,38 @@
import { cache } from 'react';
import { getAnalysisResultsForDoctor } from '@kit/doctor/services/doctor-analysis.service';
import { PageBody, PageHeader } from '@kit/ui/page';
import AnalysisView from '../../_components/analysis-view';
import { DoctorGuard } from '../../_components/doctor-guard';
async function AnalysisPage({
params,
}: {
params: Promise<{
id: string;
}>;
}) {
const { id } = await params;
const analysisResultDetails = await loadResult(Number(id));
if (!analysisResultDetails) {
return null;
}
return (
<>
<PageHeader />
<PageBody>
<AnalysisView
patient={analysisResultDetails.patient}
order={analysisResultDetails.order}
analyses={analysisResultDetails.analysisResponse}
feedback={analysisResultDetails.doctorFeedback}
/>
</PageBody>
</>
);
}
export default DoctorGuard(AnalysisPage);
const loadResult = cache(getAnalysisResultsForDoctor);

View File

@@ -0,0 +1,22 @@
import { PageBody, PageHeader } from '@kit/ui/page';
import { getUserDoneResponsesAction } from '@kit/doctor/actions/table-data-fetching-actions';
import ResultsTableWrapper from '../_components/results-table-wrapper';
import { DoctorGuard } from '../_components/doctor-guard';
async function CompletedJobsPage() {
return (
<>
<PageHeader />
<PageBody>
<ResultsTableWrapper
titleKey="doctor:completedReviews"
action={getUserDoneResponsesAction}
queryKey="doctor-done-jobs"
/>
</PageBody>
</>
);
}
export default DoctorGuard(CompletedJobsPage);

View File

@@ -0,0 +1,21 @@
import { getUserInProgressResponsesAction } from '@kit/doctor/actions/table-data-fetching-actions';
import { PageBody, PageHeader } from '@kit/ui/page';
import ResultsTableWrapper from '../_components/results-table-wrapper';
import { DoctorGuard } from '../_components/doctor-guard';
async function MyReviewsPage() {
return (
<>
<PageHeader />
<PageBody>
<ResultsTableWrapper
titleKey="doctor:myReviews"
action={getUserInProgressResponsesAction}
queryKey="doctor-in-progress-jobs"
/>
</PageBody>
</>
);
}
export default DoctorGuard(MyReviewsPage);

View File

@@ -0,0 +1,22 @@
import { getOpenResponsesAction } from '@kit/doctor/actions/table-data-fetching-actions';
import { PageBody, PageHeader } from '@kit/ui/page';
import { DoctorGuard } from '../_components/doctor-guard';
import ResultsTableWrapper from '../_components/results-table-wrapper';
async function OpenJobsPage() {
return (
<>
<PageHeader />
<PageBody>
<ResultsTableWrapper
titleKey="doctor:openReviews"
action={getOpenResponsesAction}
queryKey="doctor-open-jobs"
/>
</PageBody>
</>
);
}
export default DoctorGuard(OpenJobsPage);

View File

@@ -1,14 +1,13 @@
import { DoctorGuard } from '@kit/doctor/components/doctor-guard';
import { PageBody, PageHeader } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import Dashboard from './_components/doctor-dashboard';
import { DoctorGuard } from './_components/doctor-guard';
function DoctorPage() {
async function DoctorPage() {
return (
<>
<PageHeader description={<Trans i18nKey="common:doctor" />} />
<PageHeader />
<PageBody>
<div>TBD</div>
<Dashboard />
</PageBody>
</>
);

View File

@@ -10,7 +10,7 @@ import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import { SiteHeader } from '~/(marketing)/_components/site-header';
import { RootProviders } from '~/components/root-providers';
import { RootProviders } from '@kit/shared/components/root-providers';
const GlobalErrorPage = ({
error,

View File

@@ -1,9 +1,9 @@
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { ArrowDown } from 'lucide-react';
import { cn } from '@kit/ui/utils';
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
import { AnalysisResultForDisplay } from './analysis';
export enum AnalysisResultLevel {
VERY_LOW = 0,
@@ -63,7 +63,7 @@ const AnalysisLevelBar = ({
normLowerIncluded?: boolean;
normUpperIncluded?: boolean;
level: AnalysisResultLevel;
results: UserAnalysisElement;
results: AnalysisResultForDisplay;
}) => {
const { norm_lower: lower, norm_upper: upper, response_value: value } = results;

View File

@@ -16,6 +16,18 @@ import AnalysisLevelBar, {
AnalysisResultLevel,
} from './analysis-level-bar';
export type AnalysisResultForDisplay = Pick<
UserAnalysisElement,
| 'norm_status'
| 'response_value'
| 'unit'
| 'norm_lower_included'
| 'norm_upper_included'
| 'norm_lower'
| 'norm_upper'
| 'response_time'
>;
export enum AnalysisStatus {
NORMAL = 0,
MEDIUM = 1,
@@ -26,8 +38,8 @@ const Analysis = ({
analysisElement,
results,
}: {
analysisElement: AnalysisElement;
results?: UserAnalysisElement;
analysisElement: Pick<AnalysisElement, 'analysis_name_lab'>;
results?: AnalysisResultForDisplay;
}) => {
const name = analysisElement.analysis_name_lab || '';
const status = results?.norm_status || AnalysisStatus.NORMAL;

View File

@@ -9,17 +9,18 @@ import { Trans } from '@kit/ui/makerkit/trans';
import { PageBody } from '@kit/ui/page';
import { Button } from '@kit/ui/shadcn/button';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { getAnalysisElements } from '~/lib/services/analysis-element.service';
import {
PAGE_VIEW_ACTION,
createPageViewLog,
} from '~/lib/services/audit/pageView.service';
import { AnalysisOrder, getAnalysisOrders } from '~/lib/services/order.service';
import { ButtonTooltip } from '~/components/ui/button-tooltip';
import { ButtonTooltip } from '@kit/shared/components/ui/button-tooltip';
import Analysis from './_components/analysis';
import { loadUserAnalysis } from '../../_lib/server/load-user-analysis';
import Analysis from './_components/analysis';
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();

View File

@@ -4,6 +4,7 @@ import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
import OrderCards from '../../_components/order-cards';

View File

@@ -2,16 +2,16 @@ import { use } from 'react';
import { cookies } from 'next/headers';
import { retrieveCart } from '@lib/data/cart';
import { StoreCart } from '@medusajs/types';
import { z } from 'zod';
import { UserWorkspaceContextProvider } from '@kit/accounts/components';
import { AppLogo } from '@kit/shared/components/app-logo';
import { personalAccountNavigationConfig } from '@kit/shared/config';
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
import { SidebarProvider } from '@kit/ui/shadcn-sidebar';
import { StoreCart } from '@medusajs/types';
import { retrieveCart } from '@lib/data/cart';
import { AppLogo } from '~/components/app-logo';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
import { withI18n } from '~/lib/i18n/with-i18n';
// home imports

View File

@@ -2,8 +2,9 @@ import { Scale } from 'lucide-react';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { Button } from '@kit/ui/button';
import SelectAnalysisPackages from '@/components/select-analysis-packages';
import SelectAnalysisPackages from '@kit/shared/components/select-analysis-packages';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';

View File

@@ -1,5 +1,6 @@
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import { HomeLayoutPageHeader } from '../../_components/home-page-header';

View File

@@ -1,5 +1,6 @@
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import { HomeLayoutPageHeader } from '../../_components/home-page-header';

View File

@@ -3,9 +3,11 @@ import { PageBody, PageHeader } from '@kit/ui/page';
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { getOrder } from '~/lib/services/order.service';
import { retrieveOrder } from '@lib/data/orders';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import Divider from "@modules/common/components/divider"
import OrderDetails from '@/app/home/(user)/_components/order/order-details';
import OrderItems from '@/app/home/(user)/_components/order/order-items';

View File

@@ -3,9 +3,11 @@ import { PageBody, PageHeader } from '@kit/ui/page';
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { getOrder } from '~/lib/services/order.service';
import { retrieveOrder } from '@lib/data/orders';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import Divider from "@modules/common/components/divider"
import OrderDetails from '@/app/home/(user)/_components/order/order-details';
import OrderItems from '@/app/home/(user)/_components/order/order-items';

View File

@@ -4,14 +4,15 @@ import { listOrders } from '~/medusa/lib/data/orders';
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
import { listProductTypes } from '@lib/data/products';
import { PageBody } from '@kit/ui/makerkit/page';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { Trans } from '@kit/ui/trans';
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
import { withI18n } from '~/lib/i18n/with-i18n';
import { getAnalysisOrders } from '~/lib/services/order.service';
import OrderBlock from '../../_components/orders/order-block';
import React from 'react';
import { Divider } from '@medusajs/ui';
import { withI18n } from '~/lib/i18n/with-i18n';
export async function generateMetadata() {
const { t } = await createI18nServerInstance();

View File

@@ -19,11 +19,11 @@ import {
TableRow,
} from '@kit/ui/table';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { PackageHeader } from '@/components/package-header';
import { InfoTooltip } from '@/components/ui/info-tooltip';
import { PackageHeader } from '@kit/shared/components/package-header';
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
import { StoreProduct } from '@medusajs/types';
import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product';
import { withI18n } from '@/lib/i18n/with-i18n';
const CheckWithBackground = () => {
return (

View File

@@ -2,11 +2,9 @@
import Link from 'next/link';
import { InfoTooltip } from '@/components/ui/info-tooltip';
import type { AccountWithParams } from '@/packages/features/accounts/src/server/api';
import { Database } from '@/packages/supabase/src/database.types';
import { BlendingModeIcon, RulerHorizontalIcon } from '@radix-ui/react-icons';
import Isikukood from 'isikukood';
import {
Activity,
Clock9,
@@ -17,6 +15,8 @@ import {
User,
} from 'lucide-react';
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
import { getPersonParameters } from '@kit/shared/utils';
import { Button } from '@kit/ui/button';
import {
Card,
@@ -147,19 +147,6 @@ const dummyRecommendations = [
},
];
const getPersonParameters = (personalCode: string) => {
try {
const person = new Isikukood(personalCode);
return {
gender: person.getGender(),
age: person.getAge(),
};
} catch (error) {
console.error(error);
return null;
}
};
export default function Dashboard({
account,
bmiThresholds,

View File

@@ -7,8 +7,8 @@ import { useRouter } from 'next/navigation';
import { AccountSelector } from '@kit/accounts/account-selector';
import { SidebarContext } from '@kit/ui/shadcn-sidebar';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { pathsConfig, featureFlagsConfig } from '@kit/shared/config';
const features = {
enableTeamCreation: featureFlagsConfig.enableTeamCreation,

View File

@@ -2,9 +2,9 @@ import Link from 'next/link';
import { ShoppingCart } from 'lucide-react';
import { Trans } from '@kit/ui/trans';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { Search } from '~/components/ui/search';
import { AppLogo } from '@kit/shared/components/app-logo';
import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container';
import { Search } from '@kit/shared/components/ui/search';
import { SIDEBAR_WIDTH_PROPERTY } from '@/packages/ui/src/shadcn/constants';
import { Button } from '@kit/ui/button';

View File

@@ -2,8 +2,13 @@
import Link from 'next/link';
import { StoreCart } from '@medusajs/types';
import { LogOut, Menu, ShoppingCart } from 'lucide-react';
import {
featureFlagsConfig,
personalAccountNavigationConfig,
} from '@kit/shared/config';
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
import {
DropdownMenu,
@@ -16,16 +21,15 @@ import {
} from '@kit/ui/dropdown-menu';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import { StoreCart } from '@medusajs/types';
import featuresFlagConfig from '~/config/feature-flags.config';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
// home imports
import { HomeAccountSelector } from '../_components/home-account-selector';
import type { UserWorkspace } from '../_lib/server/load-user-workspace';
export function HomeMobileNavigation(props: { workspace: UserWorkspace, cart: StoreCart | null }) {
export function HomeMobileNavigation(props: {
workspace: UserWorkspace;
cart: StoreCart | null;
}) {
const signOut = useSignOut();
const Links = personalAccountNavigationConfig.routes.map((item, index) => {
@@ -47,7 +51,6 @@ export function HomeMobileNavigation(props: { workspace: UserWorkspace, cart: St
}
});
const cartItemsCount = props.cart?.items?.length ?? 0;
const hasCartItems = cartItemsCount > 0;
@@ -58,7 +61,7 @@ export function HomeMobileNavigation(props: { workspace: UserWorkspace, cart: St
</DropdownMenuTrigger>
<DropdownMenuContent sideOffset={10} className={'w-screen rounded-none'}>
<If condition={featuresFlagConfig.enableTeamAccounts}>
<If condition={featureFlagsConfig.enableTeamAccounts}>
<DropdownMenuGroup>
<DropdownMenuLabel>
<Trans i18nKey={'common:yourAccounts'} />
@@ -113,7 +116,11 @@ function DropdownLink(
{props.Icon}
<span>
<Trans i18nKey={props.label} defaults={props.label} values={props.labelOptions} />
<Trans
i18nKey={props.label}
defaults={props.label}
values={props.labelOptions}
/>
</span>
</Link>
</DropdownMenuItem>

View File

@@ -1,3 +1,4 @@
import { personalAccountNavigationConfig } from '@kit/shared/config';
import {
Sidebar,
SidebarContent,
@@ -6,8 +7,6 @@ import {
} from '@kit/ui/shadcn-sidebar';
import { Trans } from '@kit/ui/trans';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
export function HomeSidebar() {
const collapsible = personalAccountNavigationConfig.sidebarCollapsedStyle;

View File

@@ -13,7 +13,7 @@ import { StoreProduct, StoreProductVariant } from '@medusajs/types';
import { useState } from 'react';
import { handleAddToCart } from '~/lib/services/medusaCart.service';
import { useRouter } from 'next/navigation';
import { InfoTooltip } from '~/components/ui/info-tooltip';
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
import { Trans } from '@kit/ui/trans';
export default function OrderAnalysesCards({

View File

@@ -1,16 +1,15 @@
import { NotificationsPopover } from '@kit/notifications/components';
import featuresFlagConfig from '~/config/feature-flags.config';
import { featureFlagsConfig } from '@kit/shared/config';
export function UserNotifications(props: { userId: string }) {
if (!featuresFlagConfig.enableNotifications) {
if (!featureFlagsConfig.enableNotifications) {
return null;
}
return (
<NotificationsPopover
accountIds={[props.userId]}
realtime={featuresFlagConfig.realtimeNotifications}
realtime={featureFlagsConfig.realtimeNotifications}
/>
);
}

View File

@@ -4,12 +4,12 @@ import { createAccountsApi } from '@kit/accounts/api';
import { UserAnalysis } from '@kit/accounts/types/accounts';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
export type UserAccount = Awaited<ReturnType<typeof loadUserAnalysis>>;
export type UserAnalyses = Awaited<ReturnType<typeof loadUserAnalysis>>;
/**
* @name loadUserAccount
* @name loadUserAnalysis
* @description
* Load the user account. It's a cached per-request function that fetches the user workspace data.
* Load the user's analyses. It's a cached per-request function that fetches the user workspace data.
* It can be used across the server components to load the user workspace data.
*/
export const loadUserAnalysis = cache(analysisLoader);

View File

@@ -1,9 +1,9 @@
import { cache } from 'react';
import { createAccountsApi } from '@kit/accounts/api';
import { featureFlagsConfig } from '@kit/shared/config';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import featureFlagsConfig from '~/config/feature-flags.config';
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
const shouldLoadAccounts = featureFlagsConfig.enableTeamAccounts;

View File

@@ -19,7 +19,7 @@ import {
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import billingConfig from '~/config/billing.config';
import { billingConfig } from '@kit/shared/config';
import { createPersonalAccountCheckoutSession } from '../_lib/server/server-actions';

View File

@@ -5,10 +5,9 @@ import { redirect } from 'next/navigation';
import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import featureFlagsConfig from '~/config/feature-flags.config';
import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema';
import { createUserBillingService } from './user-billing.service';
import { featureFlagsConfig } from '@kit/shared/config';
/**
* @name enabled

View File

@@ -11,9 +11,9 @@ import { getBillingGatewayProvider } from '@kit/billing-gateway';
import { getLogger } from '@kit/shared/logger';
import { requireUser } from '@kit/supabase/require-user';
import appConfig from '~/config/app.config';
import billingConfig from '~/config/billing.config';
import pathsConfig from '~/config/paths.config';
import { appConfig, billingConfig } from '@kit/shared/config';
import { pathsConfig } from '@kit/shared/config';
import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema';

View File

@@ -1,6 +1,6 @@
import { featureFlagsConfig } from '@kit/shared/config';
import { notFound } from 'next/navigation';
import featureFlagsConfig from '~/config/feature-flags.config';
function UserBillingLayout(props: React.PropsWithChildren) {
const isEnabled = featureFlagsConfig.enablePersonalAccountBilling;

View File

@@ -8,12 +8,12 @@ import { If } from '@kit/ui/if';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import billingConfig from '~/config/billing.config';
import { billingConfig } from '@kit/shared/config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
// local imports
import { withI18n } from '~/lib/i18n/with-i18n';
import { HomeLayoutPageHeader } from '../_components/home-page-header';
import { createPersonalAccountBillingPortalSession } from '../billing/_lib/server/server-actions';
import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form';

View File

@@ -3,6 +3,7 @@ import { Trans } from '@kit/ui/trans';
import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { HomeLayoutPageHeader } from '../_components/home-page-header';

View File

@@ -1,14 +1,16 @@
import { use } from 'react';
import { PersonalAccountSettingsContainer } from '@kit/accounts/personal-account-settings';
import {
authConfig,
featureFlagsConfig,
pathsConfig,
} from '@kit/shared/config';
import { PageBody } from '@kit/ui/page';
import authConfig from '~/config/auth.config';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
import { withI18n } from '~/lib/i18n/with-i18n';
const features = {
enableAccountDeletion: featureFlagsConfig.enableAccountDeletion,

View File

@@ -7,8 +7,8 @@ import { useRouter } from 'next/navigation';
import { AccountSelector } from '@kit/accounts/account-selector';
import { SidebarContext } from '@kit/ui/shadcn-sidebar';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { pathsConfig, featureFlagsConfig } from '@kit/shared/config';
const features = {
enableTeamCreation: featureFlagsConfig.enableTeamCreation,

View File

@@ -10,8 +10,9 @@ import { Card, CardTitle } from '@kit/ui/card';
import { Button } from '@kit/ui/shadcn/button';
import { Trans } from '@kit/ui/trans';
import pathsConfig from '~/config/paths.config';
import { createPath } from '~/config/team-account-navigation.config';
import { pathsConfig } from '@kit/shared/config';
import { createPath } from '@kit/shared/config/team-account-navigation.config';
interface TeamAccountBenefitStatisticsProps {
accountSlug: string;

View File

@@ -6,6 +6,11 @@ import { useRouter } from 'next/navigation';
import { Home, LogOut, Menu } from 'lucide-react';
import { AccountSelector } from '@kit/accounts/account-selector';
import {
featureFlagsConfig,
getTeamAccountSidebarConfig,
pathsConfig,
} from '@kit/shared/config';
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
import {
Dialog,
@@ -23,10 +28,6 @@ import {
} from '@kit/ui/dropdown-menu';
import { Trans } from '@kit/ui/trans';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
type Accounts = Array<{
label: string | null;
value: string | null;

View File

@@ -8,8 +8,8 @@ import {
SidebarHeader,
} from '@kit/ui/shadcn-sidebar';
import { ProfileAccountDropdownContainer } from '~/components//personal-account-dropdown-container';
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
import { ProfileAccountDropdownContainer } from '@kit/shared/components//personal-account-dropdown-container';
import { getTeamAccountSidebarConfig } from '@kit/shared/config/team-account-navigation.config';
import { TeamAccountNotifications } from '~/home/[account]/_components/team-account-notifications';
import { TeamAccountAccountsSelector } from '../_components/team-account-accounts-selector';

View File

@@ -1,8 +1,8 @@
import { useMemo } from 'react';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
import { AppLogo } from '@kit/shared/components/app-logo';
import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container';
import { getTeamAccountSidebarConfig } from '@kit/shared/config/team-account-navigation.config';
// local imports
import { TeamAccountWorkspace } from '../_lib/server/team-account-workspace.loader';

View File

@@ -1,19 +1,20 @@
import { NotificationsPopover } from '@kit/notifications/components';
import featuresFlagConfig from '~/config/feature-flags.config';
import { featureFlagsConfig } from '@kit/shared/config';
export function TeamAccountNotifications(params: {
userId: string;
accountId: string;
}) {
if (!featuresFlagConfig.enableNotifications) {
if (!featureFlagsConfig.enableNotifications) {
return null;
}
return (
<NotificationsPopover
accountIds={[params.userId, params.accountId]}
realtime={featuresFlagConfig.realtimeNotifications}
realtime={featureFlagsConfig.realtimeNotifications}
/>
);
}

View File

@@ -8,8 +8,9 @@ import { ChevronRight, Euro, User } from 'lucide-react';
import { Card } from '@kit/ui/card';
import { Trans } from '@kit/ui/makerkit/trans';
import pathsConfig from '~/config/paths.config';
import { createPath } from '~/config/team-account-navigation.config';
import { pathsConfig } from '@kit/shared/config';
import { createPath } from '@kit/shared/config/team-account-navigation.config';
import TeamAccountBenefitStatistics from './team-account-benefit-statistics';
import TeamAccountHealthDetails from './team-account-health-details';

View File

@@ -7,7 +7,8 @@ import { redirect } from 'next/navigation';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { createTeamAccountsApi } from '@kit/team-accounts/api';
import pathsConfig from '~/config/paths.config';
import { pathsConfig } from '@kit/shared/config';
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
export type TeamAccountWorkspace = Awaited<

View File

@@ -16,7 +16,7 @@ import {
} from '@kit/ui/card';
import { Trans } from '@kit/ui/trans';
import billingConfig from '~/config/billing.config';
import { billingConfig } from '@kit/shared/config';
import { createTeamAccountCheckoutSession } from '../_lib/server/server-actions';

View File

@@ -5,10 +5,9 @@ import { redirect } from 'next/navigation';
import { UpdateHealthBenefitData } from '@/packages/features/team-accounts/src/server/types';
import { enhanceAction } from '@kit/next/actions';
import { featureFlagsConfig } from '@kit/shared/config';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import featureFlagsConfig from '~/config/feature-flags.config';
// billing imports
import {
TeamBillingPortalSchema,

View File

@@ -13,9 +13,10 @@ import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { createTeamAccountsApi } from '@kit/team-accounts/api';
import appConfig from '~/config/app.config';
import billingConfig from '~/config/billing.config';
import pathsConfig from '~/config/paths.config';
import appConfig from '@kit/shared/config/app.config';
import { billingConfig } from '@kit/shared/config';
import { pathsConfig } from '@kit/shared/config';
import { TeamCheckoutSchema } from '../schema/team-billing.schema';

View File

@@ -1,6 +1,6 @@
import { notFound } from 'next/navigation';
import featureFlagsConfig from '~/config/feature-flags.config';
import { featureFlagsConfig } from '@kit/shared/config';
function TeamAccountBillingLayout(props: React.PropsWithChildren) {
const isEnabled = featureFlagsConfig.enableTeamAccountBilling;

View File

@@ -6,6 +6,7 @@ import { PageBody } from '@kit/ui/page';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import HealthBenefitForm from './_components/health-benefit-form';
interface TeamAccountBillingPageProps {

View File

@@ -2,13 +2,13 @@ import { notFound, redirect } from 'next/navigation';
import { getBillingGatewayProvider } from '@kit/billing-gateway';
import { BillingSessionStatus } from '@kit/billing-gateway/components';
import { billingConfig } from '@kit/shared/config';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import billingConfig from '~/config/billing.config';
import { withI18n } from '~/lib/i18n/with-i18n';
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
import { EmbeddedCheckoutForm } from '../_components/embedded-checkout-form';
import { withI18n } from '~/lib/i18n/with-i18n';
interface SessionPageProps {
searchParams: Promise<{

View File

@@ -4,22 +4,20 @@ import { cookies } from 'next/headers';
import { z } from 'zod';
import { AppLogo } from '@kit/shared/components/app-logo';
import { getTeamAccountSidebarConfig } from '@kit/shared/config';
import {
CompanyGuard,
TeamAccountWorkspaceContextProvider,
} from '@kit/team-accounts/components';
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
import { SidebarProvider } from '@kit/ui/shadcn-sidebar';
import { AppLogo } from '~/components/app-logo';
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { TeamAccountLayoutMobileNavigation } from './_components/team-account-layout-mobile-navigation';
import { TeamAccountLayoutSidebar } from './_components/team-account-layout-sidebar';
import { TeamAccountNavigationMenu } from './_components/team-account-navigation-menu';
import { loadTeamWorkspace } from './_lib/server/team-account-workspace.loader';
import { withI18n } from '~/lib/i18n/with-i18n';
type TeamWorkspaceLayoutProps = React.PropsWithChildren<{
params: Promise<{ account: string }>;

View File

@@ -22,6 +22,7 @@ import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { TeamAccountLayoutPageHeader } from '../_components/team-account-layout-page-header';
import { loadMembersPageData } from './_lib/server/members-page.loader';

View File

@@ -10,10 +10,10 @@ import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { Dashboard } from './_components/dashboard';
import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header';
import { withI18n } from '~/lib/i18n/with-i18n';
interface TeamAccountHomePageProps {
params: Promise<{ account: string }>;

View File

@@ -1,3 +1,4 @@
import { featureFlagsConfig, pathsConfig } from '@kit/shared/config';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { createTeamAccountsApi } from '@kit/team-accounts/api';
import { TeamAccountSettingsContainer } from '@kit/team-accounts/components';
@@ -5,8 +6,6 @@ import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import featuresFlagConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
// local imports
@@ -43,7 +42,7 @@ async function TeamAccountSettingsPage(props: TeamAccountSettingsPageProps) {
};
const features = {
enableTeamDeletion: featuresFlagConfig.enableTeamDeletion,
enableTeamDeletion: featureFlagsConfig.enableTeamDeletion,
};
return (

View File

@@ -13,11 +13,13 @@ import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import { AppLogo } from '~/components/app-logo';
import pathsConfig from '~/config/paths.config';
import { AppLogo } from '@kit/shared/components/app-logo';
import { pathsConfig } from '@kit/shared/config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
interface JoinTeamAccountPageProps {
searchParams: Promise<{
invite_token?: string;

View File

@@ -1,13 +1,13 @@
import { headers } from 'next/headers';
import { RootProviders } from '@kit/shared/components/root-providers';
import { Toaster } from '@kit/ui/sonner';
import { RootProviders } from '~/components/root-providers';
import { getFontsClassName } from '~/lib/fonts';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { generateRootMetadata } from '~/lib/root-metdata';
import { getRootTheme } from '~/lib/root-theme';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import '../styles/globals.css';
export const generateMetadata = () => {

View File

@@ -7,8 +7,8 @@ import { Heading } from '@kit/ui/heading';
import { Trans } from '@kit/ui/trans';
import { SiteHeader } from '~/(marketing)/_components/site-header';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();

View File

@@ -1,6 +1,6 @@
import { MetadataRoute } from 'next';
import appConfig from '~/config/app.config';
import appConfig from '@kit/shared/config/app.config';
export default function robots(): MetadataRoute.Robots {
return {

View File

@@ -1,6 +1,6 @@
import Link from 'next/link';
import SelectAnalysisPackages from '@/components/select-analysis-packages';
import SelectAnalysisPackages from '@kit/shared/components/select-analysis-packages';
import { CaretRightIcon } from '@radix-ui/react-icons';
import { Scale } from 'lucide-react';
@@ -10,6 +10,7 @@ import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { MedReportLogo } from '../../components/med-report-logo';
import pathsConfig from '../../config/paths.config';
import ComparePackagesModal from '../home/(user)/_components/compare-packages-modal';

View File

@@ -2,7 +2,7 @@ import { getServerSideSitemap } from 'next-sitemap';
import { createCmsClient } from '@kit/cms';
import appConfig from '~/config/app.config';
import appConfig from '@kit/shared/config/app.config';
/**
* @description The maximum age of the sitemap in seconds.

View File

@@ -1,11 +1,11 @@
import { UpdatePasswordForm } from '@kit/auth/password-reset';
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '@kit/shared/components/app-logo';
import { pathsConfig } from '@kit/shared/config';
import { AppLogo } from '~/components/app-logo';
import pathsConfig from '~/config/paths.config';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
import { withI18n } from '~/lib/i18n/with-i18n';
export const generateMetadata = async () => {
const { t } = await createI18nServerInstance();

Some files were not shown because too many files have changed in this diff Show More