From 4b7d5ff7bba77f89bbb48c45ac65d892f38b0835 Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Mon, 25 Aug 2025 16:16:56 +0300 Subject: [PATCH 1/4] fix(sign-up): move emailRedirectTo option into signUp options --- packages/supabase/src/hooks/use-sign-up-with-email-password.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/supabase/src/hooks/use-sign-up-with-email-password.ts b/packages/supabase/src/hooks/use-sign-up-with-email-password.ts index 2d73b0b..d670e85 100644 --- a/packages/supabase/src/hooks/use-sign-up-with-email-password.ts +++ b/packages/supabase/src/hooks/use-sign-up-with-email-password.ts @@ -21,6 +21,7 @@ export function useSignUpWithEmailAndPassword() { const response = await client.auth.signUp({ ...credentials, options: { + emailRedirectTo, captchaToken, }, }); From d017834b8c8a8e1887353ebb775004e47cba6c12 Mon Sep 17 00:00:00 2001 From: Helena <37183360+helenarebane@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:49:04 +0300 Subject: [PATCH 2/4] MED-90: improve doctor analysis detail view (#57) * add doctor jobs view * change translation * another translation change * clean up * add analaysis detail view to paths config * translation * merge fix * fix path * MED-90: improve doctor analysis detail view * add key --- app/doctor/_components/analysis-view.tsx | 108 +++++++++++------ .../_components/doctor-analysis-wrapper.tsx | 103 +++++++++++++++++ app/doctor/_components/doctor-job-select.tsx | 108 +++++++++++++++++ app/doctor/_components/doctor-sidebar.tsx | 13 +-- app/doctor/_components/results-table.tsx | 98 +--------------- app/doctor/analysis/[id]/page.tsx | 1 + .../analysis-results/_components/analysis.tsx | 109 ++++++++++-------- lib/services/medipost.service.ts | 1 + .../server/actions/doctor-server-actions.ts | 8 +- .../doctor-analysis-detail-view.schema.ts | 21 ++++ .../server/schema/doctor-analysis.schema.ts | 5 +- .../services/doctor-analysis.service.ts | 61 +++++++++- .../shared/src/components/ui/info-tooltip.tsx | 2 +- packages/supabase/src/database.types.ts | 23 +++- public/locales/en/common.json | 3 +- public/locales/en/doctor.json | 19 +-- public/locales/et/common.json | 3 +- public/locales/et/doctor.json | 15 ++- public/locales/ru/common.json | 3 +- public/locales/ru/doctor.json | 17 +-- ...d_comment_to_analysis_response_element.sql | 2 + 21 files changed, 499 insertions(+), 224 deletions(-) create mode 100644 app/doctor/_components/doctor-analysis-wrapper.tsx create mode 100644 app/doctor/_components/doctor-job-select.tsx create mode 100644 supabase/migrations/20250821134556_add_comment_to_analysis_response_element.sql diff --git a/app/doctor/_components/analysis-view.tsx b/app/doctor/_components/analysis-view.tsx index 2d43d7f..762c4bd 100644 --- a/app/doctor/_components/analysis-view.tsx +++ b/app/doctor/_components/analysis-view.tsx @@ -19,7 +19,7 @@ import { } from '@kit/doctor/schema/doctor-analysis-detail-view.schema'; import { DoctorAnalysisFeedbackForm, - doctorAnalysisFeedbackSchema, + doctorAnalysisFeedbackFormSchema, } from '@kit/doctor/schema/doctor-analysis.schema'; import ConfirmationModal from '@kit/shared/components/confirmation-modal'; import { getFullName } from '@kit/shared/utils'; @@ -36,9 +36,11 @@ 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'; +import DoctorAnalysisWrapper from './doctor-analysis-wrapper'; +import DoctorJobSelect from './doctor-job-select'; + export default function AnalysisView({ patient, order, @@ -54,16 +56,20 @@ export default function AnalysisView({ const { data: user } = useUser(); - const isInProgress = + const isInProgress = !!( !!feedback?.status && feedback?.doctor_user_id && - feedback?.status !== 'COMPLETED'; + feedback?.status !== 'COMPLETED' + ); + const isCurrentDoctorJob = + !!feedback?.doctor_user_id && feedback?.doctor_user_id === user?.id; const isReadOnly = !isInProgress || (!!feedback?.doctor_user_id && feedback?.doctor_user_id !== user?.id); const form = useForm({ - resolver: zodResolver(doctorAnalysisFeedbackSchema), + resolver: zodResolver(doctorAnalysisFeedbackFormSchema), + reValidateMode: 'onChange', defaultValues: { feedbackValue: feedback?.value ?? '', userId: patient.userId, @@ -103,12 +109,22 @@ export default function AnalysisView({ } }; - const handleDraftSubmit = () => { + const handleDraftSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + form.formState.errors.feedbackValue = undefined; const formData = form.getValues(); onSubmit(formData, 'DRAFT'); }; - const handleCompleteSubmit = () => { + const handleCompleteSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const isValid = await form.trigger(); + if (!isValid) { + return; + } + setIsConfirmOpen(true); }; @@ -119,16 +135,31 @@ export default function AnalysisView({ return ( <> -

- -

-
+
+

+ +

+
+ + queryClient.invalidateQueries({ + predicate: (query) => query.queryKey.includes('doctor-jobs'), + }) + } + /> +
+
+
@@ -156,7 +187,7 @@ export default function AnalysisView({
-
+
-
@@ -166,30 +197,37 @@ export default function AnalysisView({
{patient.email}
- +
+ + queryClient.invalidateQueries({ + predicate: (query) => query.queryKey.includes('doctor-jobs'), + }) + } + /> +

{analyses.map((analysisData) => { return ( - ); })}
-

-

{feedback?.value ?? '-'}

- {!isReadOnly && (
@@ -206,23 +244,21 @@ export default function AnalysisView({ )} /> -
+
diff --git a/app/doctor/_components/doctor-analysis-wrapper.tsx b/app/doctor/_components/doctor-analysis-wrapper.tsx new file mode 100644 index 0000000..fec4c35 --- /dev/null +++ b/app/doctor/_components/doctor-analysis-wrapper.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { CaretDownIcon, QuestionMarkCircledIcon } from '@radix-ui/react-icons'; +import { useTranslation } from 'react-i18next'; + +import { AnalysisResponse } from '@kit/doctor/schema/doctor-analysis-detail-view.schema'; +import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip'; +import { formatDate } from '@kit/shared/utils'; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '@kit/ui/collapsible'; +import { Trans } from '@kit/ui/trans'; + +import Analysis from '~/home/(user)/(dashboard)/analysis-results/_components/analysis'; + +export default function DoctorAnalysisWrapper({ + analysisData, +}: { + analysisData: AnalysisResponse; +}) { + const { t } = useTranslation(); + + return ( + + +
+ + ) + } + endIcon={ + analysisData.comment && ( + <> +
+ + } + /> +
+

+ + : + {' '} + {analysisData.comment} +

+ + ) + } + analysisElement={{ + analysis_name_lab: analysisData.analysis_name, + }} + results={analysisData} + /> +
+
+ {analysisData.latestPreviousAnalysis && ( + +
+ +
+ + } + /> +
+

+ + :{' '} + + {analysisData.latestPreviousAnalysis.comment} +

+ + ) + } + analysisElement={{ + analysis_name_lab: t('doctor:previousResults', { + date: formatDate( + analysisData.latestPreviousAnalysis.response_time, + ), + }), + }} + results={analysisData.latestPreviousAnalysis} + /> +
+
+ )} +
+ ); +} diff --git a/app/doctor/_components/doctor-job-select.tsx b/app/doctor/_components/doctor-job-select.tsx new file mode 100644 index 0000000..3d58904 --- /dev/null +++ b/app/doctor/_components/doctor-job-select.tsx @@ -0,0 +1,108 @@ +'use client'; + +import { useTransition } from 'react'; + +import { useRouter } from 'next/navigation'; + +import { LoaderCircle } from 'lucide-react'; + +import { + selectJobAction, + unselectJobAction, +} from '@kit/doctor/actions/doctor-server-actions'; +import { Button, ButtonProps } from '@kit/ui/button'; +import { toast } from '@kit/ui/sonner'; +import { Trans } from '@kit/ui/trans'; +import { cn } from '@kit/ui/utils'; + +export default function DoctorJobSelect({ + className, + size = 'sm', + doctorUserId, + doctorName, + analysisOrderId, + userId, + isRemovable, + onJobUpdate, + linkTo, +}: { + className?: string; + size?: ButtonProps['size']; + doctorUserId?: string | null; + 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(); + linkTo && 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 ( + + ); + } + + if (!doctorUserId) { + return ( + + ); + } + + return <>{doctorName}; +} diff --git a/app/doctor/_components/doctor-sidebar.tsx b/app/doctor/_components/doctor-sidebar.tsx index 99e0c12..254c089 100644 --- a/app/doctor/_components/doctor-sidebar.tsx +++ b/app/doctor/_components/doctor-sidebar.tsx @@ -6,6 +6,9 @@ import { usePathname } from 'next/navigation'; import { UserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace'; import { LayoutDashboard } from 'lucide-react'; +import { AppLogo } from '@kit/shared/components/app-logo'; +import { ProfileAccountDropdownContainer } from '@kit/shared/components/personal-account-dropdown-container'; +import { pathsConfig } from '@kit/shared/config'; import { Sidebar, SidebarContent, @@ -20,11 +23,6 @@ import { } from '@kit/ui/shadcn-sidebar'; import { Trans } from '@kit/ui/trans'; -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, }: { @@ -75,10 +73,7 @@ export function DoctorSidebar({ isActive={path === pathsConfig.app.myJobs} asChild > - + diff --git a/app/doctor/_components/results-table.tsx b/app/doctor/_components/results-table.tsx index f596723..cdfd337 100644 --- a/app/doctor/_components/results-table.tsx +++ b/app/doctor/_components/results-table.tsx @@ -3,15 +3,10 @@ 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 { Eye } 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'; @@ -28,94 +23,7 @@ import { 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 ( - - ); - } - - if (!doctorUserId) { - return ( - - ); - } - - return <>{doctorName}; -} +import DoctorJobSelect from './doctor-job-select'; export default function ResultsTable({ results = [], @@ -272,7 +180,7 @@ export default function ResultsTable({ /> - ; results?: AnalysisResultForDisplay; + startIcon?: ReactElement | null; + endIcon?: ReactNode | null; }) => { const name = analysisElement.analysis_name_lab || ''; const status = results?.norm_status || AnalysisStatus.NORMAL; @@ -76,59 +80,66 @@ const Analysis = ({ }, [results, value, normLower]); return ( -
-
- {name} - {results?.response_time && ( -
setShowTooltip(!showTooltip)} - onMouseLeave={() => setShowTooltip(false)} - > - {' '} +
+
+
+ {startIcon ||
} + {name} + {results?.response_time && ( -
+ )} +
+ {results ? ( + <> +
+
{value}
+
{unit}
+
+
+ {normLower} - {normUpper} +
+ +
+
+ + {endIcon ||
} + + ) : ( + <> +
+
+ +
+
+
+ + )}
- {results ? ( - <> -
-
{value}
-
{unit}
-
-
- {normLower} - {normUpper} -
- -
-
- - - ) : ( - <> -
-
- -
-
-
- - - )}
); }; diff --git a/lib/services/medipost.service.ts b/lib/services/medipost.service.ts index 8508663..8d73f40 100644 --- a/lib/services/medipost.service.ts +++ b/lib/services/medipost.service.ts @@ -637,6 +637,7 @@ async function syncPrivateMessage({ unit: element.Mootyhik ?? null, original_response_element: element, analysis_name: element.UuringNimi || element.KNimetus, + comment: element.UuringuKommentaar })), ); } diff --git a/packages/features/doctor/src/lib/server/actions/doctor-server-actions.ts b/packages/features/doctor/src/lib/server/actions/doctor-server-actions.ts index 363de2f..2e63362 100644 --- a/packages/features/doctor/src/lib/server/actions/doctor-server-actions.ts +++ b/packages/features/doctor/src/lib/server/actions/doctor-server-actions.ts @@ -35,6 +35,7 @@ export const selectJobAction = doctorAction( logger.info({ analysisOrderId }, `Successfully selected`); + revalidateDoctorAnalysis(); return { success: true }; }, { @@ -60,7 +61,7 @@ export const unselectJobAction = doctorAction( { analysisOrderId }, `Successfully removed current doctor from job`, ); - + revalidateDoctorAnalysis(); return { success: true }; }, { @@ -101,6 +102,7 @@ export const giveFeedbackAction = doctorAction( ); logger.info({ analysisOrderId }, `Successfully submitted feedback`); + revalidateDoctorAnalysis(); return result; }, { @@ -108,3 +110,7 @@ export const giveFeedbackAction = doctorAction( }, ), ); + +function revalidateDoctorAnalysis() { + revalidatePath('/doctor/analysis'); +} diff --git a/packages/features/doctor/src/lib/server/schema/doctor-analysis-detail-view.schema.ts b/packages/features/doctor/src/lib/server/schema/doctor-analysis-detail-view.schema.ts index a5ae622..00025c2 100644 --- a/packages/features/doctor/src/lib/server/schema/doctor-analysis-detail-view.schema.ts +++ b/packages/features/doctor/src/lib/server/schema/doctor-analysis-detail-view.schema.ts @@ -66,6 +66,27 @@ export const AnalysisResponseSchema = z.object({ updated_at: z.string().nullable(), analysis_name: z.string().nullable(), analysis_responses: AnalysisResponsesSchema, + comment: z.string().nullable(), + latestPreviousAnalysis: z + .object({ + id: z.number(), + analysis_response_id: z.number(), + analysis_element_original_id: z.string(), + unit: z.string().nullable(), + response_value: z.number(), + response_time: z.string(), + norm_upper: z.number().nullable(), + norm_upper_included: z.boolean().nullable(), + norm_lower: z.number().nullable(), + norm_lower_included: z.boolean().nullable(), + norm_status: z.number().nullable(), + created_at: z.string(), + updated_at: z.string().nullable(), + analysis_name: z.string().nullable(), + comment: z.string().nullable(), + }) + .optional() + .nullable(), }); export type AnalysisResponse = z.infer; diff --git a/packages/features/doctor/src/lib/server/schema/doctor-analysis.schema.ts b/packages/features/doctor/src/lib/server/schema/doctor-analysis.schema.ts index faecafe..8758cbb 100644 --- a/packages/features/doctor/src/lib/server/schema/doctor-analysis.schema.ts +++ b/packages/features/doctor/src/lib/server/schema/doctor-analysis.schema.ts @@ -15,18 +15,19 @@ export type DoctorJobUnselect = z.infer; export const FeedbackStatus = z.enum(['STARTED', 'DRAFT', 'COMPLETED']); export const doctorAnalysisFeedbackFormSchema = z.object({ - feedbackValue: z.string().min(15), + feedbackValue: z.string().min(10, { message: 'doctor:feedbackLengthError' }), userId: z.string().uuid(), }); export type DoctorAnalysisFeedbackForm = z.infer< typeof doctorAnalysisFeedbackFormSchema >; export const doctorAnalysisFeedbackSchema = z.object({ - feedbackValue: z.string().min(15), + feedbackValue: z.string(), userId: z.string().uuid(), analysisOrderId: z.number(), status: FeedbackStatus, }); + export type DoctorAnalysisFeedback = z.infer< typeof doctorAnalysisFeedbackSchema >; diff --git a/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts b/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts index fae6290..dff7ec7 100644 --- a/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts +++ b/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts @@ -360,7 +360,7 @@ export async function getAnalysisResultsForDoctor( ): Promise { const supabase = getSupabaseServerClient(); - const { data: analysisResponse, error } = await supabase + const { data: analysisResponseElements, error } = await supabase .schema('medreport') .from(`analysis_response_elements`) .select( @@ -373,20 +373,26 @@ export async function getAnalysisResultsForDoctor( throw new Error('Something went wrong.'); } - const firstAnalysisResponse = analysisResponse?.[0]; + const firstAnalysisResponse = analysisResponseElements?.[0]; const userId = firstAnalysisResponse?.analysis_responses.user_id; const medusaOrderId = firstAnalysisResponse?.analysis_responses?.analysis_order_id ?.medusa_order_id; - if (!analysisResponse?.length || !userId || !medusaOrderId) { + if (!analysisResponseElements?.length || !userId || !medusaOrderId) { throw new Error('Failed to retrieve full analysis data.'); } + const responseElementAnalysisElementOriginalIds = + analysisResponseElements.map( + ({ analysis_element_original_id }) => analysis_element_original_id, + ); + const [ { data: medusaOrderItems, error: medusaOrderError }, { data: accountWithParams, error: accountError }, { data: doctorFeedback, error: feedbackError }, + { data: previousAnalyses, error: previousAnalysesError }, ] = await Promise.all([ supabase .schema('public') @@ -403,7 +409,7 @@ export async function getAnalysisResultsForDoctor( .eq('is_personal_account', true) .eq('primary_owner_user_id', userId) .limit(1), - await supabase + supabase .schema('medreport') .from('doctor_analysis_feedback') .select(`*`) @@ -412,9 +418,39 @@ export async function getAnalysisResultsForDoctor( firstAnalysisResponse.analysis_responses.analysis_order_id.id, ) .limit(1), + supabase + .schema('medreport') + .from('analysis_response_elements') + .select( + ` + *, + analysis_responses!inner( + user_id + ) + `, + ) + .in( + 'analysis_element_original_id', + responseElementAnalysisElementOriginalIds, + ) + .not( + 'analysis_response_id', + 'eq', + firstAnalysisResponse.analysis_response_id, + ) + .eq( + 'analysis_responses.user_id', + firstAnalysisResponse.analysis_responses.user_id, + ) + .order('response_time'), ]); - if (medusaOrderError || accountError || feedbackError) { + if ( + medusaOrderError || + accountError || + feedbackError || + previousAnalysesError + ) { throw new Error('Something went wrong.'); } @@ -433,8 +469,21 @@ export async function getAnalysisResultsForDoctor( account_params, } = accountWithParams[0]; + const analysisResponseElementsWithPreviousData = []; + for (const analysisResponseElement of analysisResponseElements) { + const latestPreviousAnalysis = previousAnalyses.find( + ({ analysis_element_original_id }) => + analysis_element_original_id === + analysisResponseElement.analysis_element_original_id, + ); + analysisResponseElementsWithPreviousData.push({ + ...analysisResponseElement, + latestPreviousAnalysis, + }); + } + return { - analysisResponse, + analysisResponse: analysisResponseElementsWithPreviousData, order: { title: medusaOrderItems?.[0]?.item_id.product_title ?? '-', isPackage: diff --git a/packages/shared/src/components/ui/info-tooltip.tsx b/packages/shared/src/components/ui/info-tooltip.tsx index b0f2ed4..0573e79 100644 --- a/packages/shared/src/components/ui/info-tooltip.tsx +++ b/packages/shared/src/components/ui/info-tooltip.tsx @@ -13,7 +13,7 @@ export function InfoTooltip({ content, icon, }: { - content?: string; + content?: string | null; icon?: JSX.Element; }) { if (!content) return null; diff --git a/packages/supabase/src/database.types.ts b/packages/supabase/src/database.types.ts index 0ff6ae6..e1b3b8a 100644 --- a/packages/supabase/src/database.types.ts +++ b/packages/supabase/src/database.types.ts @@ -549,6 +549,7 @@ export type Database = { analysis_element_original_id: string analysis_name: string | null analysis_response_id: number + comment: string | null created_at: string id: number norm_lower: number | null @@ -566,6 +567,7 @@ export type Database = { analysis_element_original_id: string analysis_name?: string | null analysis_response_id: number + comment?: string | null created_at?: string id?: number norm_lower?: number | null @@ -583,6 +585,7 @@ export type Database = { analysis_element_original_id?: string analysis_name?: string | null analysis_response_id?: number + comment?: string | null created_at?: string id?: number norm_lower?: number | null @@ -1723,7 +1726,9 @@ export type Database = { Returns: Json } create_team_account: { - Args: { account_name: string; new_personal_code: string } + Args: + | { account_name: string } + | { account_name: string; new_personal_code: string } Returns: { application_role: Database["medreport"]["Enums"]["application_role"] city: string | null @@ -1910,6 +1915,22 @@ export type Database = { } Returns: undefined } + update_analysis_order_status: { + Args: { + order_id: number + medusa_order_id_param: string + status_param: Database["medreport"]["Enums"]["analysis_order_status"] + } + Returns: { + analysis_element_ids: number[] | null + analysis_ids: number[] | null + created_at: string + id: number + medusa_order_id: string + status: Database["medreport"]["Enums"]["analysis_order_status"] + user_id: string + } + } upsert_order: { Args: { target_account_id: string diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 155f795..2e28960 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -115,5 +115,6 @@ "saveAsDraft": "Save as draft", "confirm": "Confirm", "previous": "Previous", - "next": "Next" + "next": "Next", + "invalidDataError": "Invalid data submitted" } \ No newline at end of file diff --git a/public/locales/en/doctor.json b/public/locales/en/doctor.json index 0a73489..d5afa91 100644 --- a/public/locales/en/doctor.json +++ b/public/locales/en/doctor.json @@ -30,14 +30,17 @@ "phone": "Phone", "email": "Email", "results": "Analysis results", - "feedback": "Feedback", + "feedback": "Summary", "selectJob": "Select", - "unselectJob": "Deselect", + "unselectJob": "Unselect", + "previousResults": "Previous results ({{date}})", + "labComment": "Lab comment", "confirmFeedbackModal": { - "title": "Confirm publishing feedback", - "description": "When confirmed, the feedback will be published to the patient." + "title": "Confirm publishing summary", + "description": "When confirmed, the summary will be published to the patient." }, - "updateFeedbackSuccess": "Feedback updated", - "updateFeedbackLoading": "Updating feedback...", - "updateFeedbackError": "Failed to update feedback" -} + "updateFeedbackSuccess": "Summary updated", + "updateFeedbackLoading": "Updating summary...", + "updateFeedbackError": "Failed to update summary", + "feedbackLengthError": "Summary must be at least 10 characters" +} \ No newline at end of file diff --git a/public/locales/et/common.json b/public/locales/et/common.json index d9d85ac..505ebd9 100644 --- a/public/locales/et/common.json +++ b/public/locales/et/common.json @@ -133,5 +133,6 @@ "saveAsDraft": "Salvesta mustandina", "confirm": "Kinnita", "previous": "Eelmine", - "next": "Järgmine" + "next": "Järgmine", + "invalidDataError": "Vigased andmed" } \ No newline at end of file diff --git a/public/locales/et/doctor.json b/public/locales/et/doctor.json index 6945f43..c1ff2b7 100644 --- a/public/locales/et/doctor.json +++ b/public/locales/et/doctor.json @@ -30,14 +30,17 @@ "phone": "Telefon", "email": "E-mail", "results": "Analüüside tulemused", - "feedback": "Tagasiside", + "feedback": "Kokkuvõte", "selectJob": "Vali", "unselectJob": "Loobu", + "previousResults": "Eelnevad tulemused ({{date}})", + "labComment": "Labori kommentaarid", "confirmFeedbackModal": { - "title": "Kinnita tagasiside avaldamine", - "description": "Tagasiside kinnitamisel avaldatakse see patsiendile." + "title": "Kinnita kokkuvõtte avaldamine", + "description": "Kinnitamisel avaldatakse kokkuvõte patsiendile." }, - "updateFeedbackSuccess": "Tagasiside uuendatud", - "updateFeedbackLoading": "Tagasiside uuendatakse...", - "updateFeedbackError": "Tagasiside uuendamine ebaõnnestus" + "updateFeedbackSuccess": "Kokkuvõte uuendatud", + "updateFeedbackLoading": "Kokkuvõtet uuendatakse...", + "updateFeedbackError": "Kokkuvõtte uuendamine ebaõnnestus", + "feedbackLengthError": "Kokkuvõte peab olema vähemalt 10 tähemärki pikk" } \ No newline at end of file diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index c9bb217..f27aea6 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -113,5 +113,6 @@ "saveAsDraft": "Save as draft", "confirm": "Confirm", "previous": "Previous", - "next": "Next" + "next": "Next", + "invalidDataError": "Invalid data submitted" } \ No newline at end of file diff --git a/public/locales/ru/doctor.json b/public/locales/ru/doctor.json index e88dd58..d5afa91 100644 --- a/public/locales/ru/doctor.json +++ b/public/locales/ru/doctor.json @@ -30,14 +30,17 @@ "phone": "Phone", "email": "Email", "results": "Analysis results", - "feedback": "Feedback", + "feedback": "Summary", "selectJob": "Select", - "unselectJob": "Deselect", + "unselectJob": "Unselect", + "previousResults": "Previous results ({{date}})", + "labComment": "Lab comment", "confirmFeedbackModal": { - "title": "Confirm publishing feedback", - "description": "When confirmed, the feedback will be published to the patient." + "title": "Confirm publishing summary", + "description": "When confirmed, the summary will be published to the patient." }, - "updateFeedbackSuccess": "Feedback updated", - "updateFeedbackLoading": "Updating feedback...", - "updateFeedbackError": "Failed to update feedback" + "updateFeedbackSuccess": "Summary updated", + "updateFeedbackLoading": "Updating summary...", + "updateFeedbackError": "Failed to update summary", + "feedbackLengthError": "Summary must be at least 10 characters" } \ No newline at end of file diff --git a/supabase/migrations/20250821134556_add_comment_to_analysis_response_element.sql b/supabase/migrations/20250821134556_add_comment_to_analysis_response_element.sql new file mode 100644 index 0000000..75badf6 --- /dev/null +++ b/supabase/migrations/20250821134556_add_comment_to_analysis_response_element.sql @@ -0,0 +1,2 @@ +ALTER TABLE medreport.analysis_response_elements +ADD comment text; \ No newline at end of file From 38d8ca9cfb56665f051912fe438e86dcda326fef Mon Sep 17 00:00:00 2001 From: Helena Date: Mon, 25 Aug 2025 19:34:47 +0300 Subject: [PATCH 3/4] add isBefore check to previous analysis find --- .../src/lib/server/services/doctor-analysis.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts b/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts index dff7ec7..d45a22a 100644 --- a/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts +++ b/packages/features/doctor/src/lib/server/services/doctor-analysis.service.ts @@ -1,5 +1,7 @@ import 'server-only'; +import { isBefore } from 'date-fns'; + import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { AnalysisResultDetails } from '../schema/doctor-analysis-detail-view.schema'; @@ -472,9 +474,13 @@ export async function getAnalysisResultsForDoctor( const analysisResponseElementsWithPreviousData = []; for (const analysisResponseElement of analysisResponseElements) { const latestPreviousAnalysis = previousAnalyses.find( - ({ analysis_element_original_id }) => + ({ analysis_element_original_id, response_time }) => analysis_element_original_id === - analysisResponseElement.analysis_element_original_id, + analysisResponseElement.analysis_element_original_id && + isBefore( + new Date(response_time), + new Date(analysisResponseElement.response_time), + ), ); analysisResponseElementsWithPreviousData.push({ ...analysisResponseElement, From 5663f41f226cbf2aa68a89a5ef72842db4e868ee Mon Sep 17 00:00:00 2001 From: Helena Date: Mon, 25 Aug 2025 19:48:37 +0300 Subject: [PATCH 4/4] switch weight and height parameters in analysis view --- app/doctor/_components/analysis-view.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/doctor/_components/analysis-view.tsx b/app/doctor/_components/analysis-view.tsx index 762c4bd..824d59a 100644 --- a/app/doctor/_components/analysis-view.tsx +++ b/app/doctor/_components/analysis-view.tsx @@ -183,7 +183,7 @@ export default function AnalysisView({
-
{bmiFromMetric(patient?.height ?? 0, patient?.weight ?? 0)}
+
{bmiFromMetric(patient?.weight ?? 0, patient?.height ?? 0)}