MED-104: booking page
MED-104
This commit is contained in:
@@ -71,19 +71,19 @@ export default async function syncConnectedOnline() {
|
||||
return {
|
||||
id: service.ID,
|
||||
clinic_id: service.ClinicID,
|
||||
code: service.Code,
|
||||
description: service.Description || null,
|
||||
display: service.Display,
|
||||
duration: service.Duration,
|
||||
has_free_codes: !!service.HasFreeCodes,
|
||||
sync_id: service.SyncID,
|
||||
name: service.Name,
|
||||
description: service.Description || null,
|
||||
price: service.Price,
|
||||
requires_payment: !!service.RequiresPayment,
|
||||
duration: service.Duration,
|
||||
neto_duration: service.NetoDuration,
|
||||
display: service.Display,
|
||||
price_periods: service.PricePeriods || null,
|
||||
online_hide_duration: service.OnlineHideDuration,
|
||||
online_hide_price: service.OnlineHidePrice,
|
||||
price: service.Price,
|
||||
price_periods: service.PricePeriods || null,
|
||||
requires_payment: !!service.RequiresPayment,
|
||||
sync_id: service.SyncID,
|
||||
code: service.Code,
|
||||
has_free_codes: !!service.HasFreeCodes,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
41
app/home/(user)/(dashboard)/booking/[handle]/page.tsx
Normal file
41
app/home/(user)/(dashboard)/booking/[handle]/page.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { HomeLayoutPageHeader } from '@/app/home/(user)/_components/home-page-header';
|
||||
import { loadCategory } from '@/app/home/(user)/_lib/server/load-category';
|
||||
|
||||
import { AppBreadcrumbs } from '@kit/ui/makerkit/app-breadcrumbs';
|
||||
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';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const i18n = await createI18nServerInstance();
|
||||
const title = i18n.t('booking:title');
|
||||
|
||||
return {
|
||||
title,
|
||||
};
|
||||
};
|
||||
|
||||
async function BookingHandlePage({ params }: { params: { handle: string } }) {
|
||||
const handle = await params.handle;
|
||||
const { category } = await loadCategory({ handle });
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBreadcrumbs
|
||||
values={{
|
||||
[handle]: category?.name || handle,
|
||||
}}
|
||||
/>
|
||||
<HomeLayoutPageHeader
|
||||
title={<Trans i18nKey={'booking:title'} />}
|
||||
description={<Trans i18nKey={'booking:description'} />}
|
||||
/>
|
||||
|
||||
<PageBody></PageBody>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(BookingHandlePage);
|
||||
@@ -1,12 +1,16 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { AppBreadcrumbs } from '@kit/ui/makerkit/app-breadcrumbs';
|
||||
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 { HomeLayoutPageHeader } from '../../_components/home-page-header';
|
||||
import OrderCards from '../../_components/order-cards';
|
||||
import ServiceCategories from '../../_components/service-categories';
|
||||
import { loadTtoServices } from '../../_lib/server/load-tto-services';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const i18n = await createI18nServerInstance();
|
||||
@@ -18,15 +22,30 @@ export const generateMetadata = async () => {
|
||||
};
|
||||
|
||||
function BookingPage() {
|
||||
const { heroCategories, ttoCategories } = use(loadTtoServices());
|
||||
|
||||
if (!heroCategories.length && !ttoCategories.length) {
|
||||
return (
|
||||
<>
|
||||
<AppBreadcrumbs />
|
||||
<h3 className="mt-8">
|
||||
<Trans i18nKey="booking:noCategories" />
|
||||
</h3>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBreadcrumbs />
|
||||
<HomeLayoutPageHeader
|
||||
title={<Trans i18nKey={'booking:title'} />}
|
||||
description={<Trans i18nKey={'booking:description'} />}
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
<OrderCards />
|
||||
<PageBody className="space-y-2">
|
||||
<OrderCards heroCategories={heroCategories} />
|
||||
<ServiceCategories categories={ttoCategories} />
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,74 +1,68 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { ChevronRight, HeartPulse } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { pathsConfig } from '@/packages/shared/src/config';
|
||||
import { ComponentInstanceIcon } from '@radix-ui/react-icons';
|
||||
import { ChevronRight, HeartPulse } from 'lucide-react';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardDescription,
|
||||
CardProps,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardProps,
|
||||
} from '@kit/ui/card';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const dummyCards = [
|
||||
{
|
||||
title: 'booking:analysisPackages.title',
|
||||
description: 'booking:analysisPackages.description',
|
||||
descriptionColor: 'text-primary',
|
||||
icon: (
|
||||
<Link href={'/home/order-analysis-package'}>
|
||||
<Button size="icon" variant="outline" className="px-2 text-black">
|
||||
<ChevronRight className="size-4 stroke-2" />
|
||||
</Button>
|
||||
</Link>
|
||||
),
|
||||
cardVariant: 'gradient-success' as CardProps['variant'],
|
||||
iconBg: 'bg-warning',
|
||||
},
|
||||
];
|
||||
import { ServiceCategory } from './service-categories';
|
||||
|
||||
export default function OrderCards() {
|
||||
export default function OrderCards({
|
||||
heroCategories,
|
||||
}: {
|
||||
heroCategories: ServiceCategory[];
|
||||
}) {
|
||||
return (
|
||||
<div className="grid grid-cols-3 gap-6 mt-4">
|
||||
{dummyCards.map(({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
cardVariant,
|
||||
descriptionColor,
|
||||
iconBg,
|
||||
}) => (
|
||||
<div className="xs:grid-cols-3 mt-4 grid grid-cols-1 gap-2">
|
||||
{heroCategories.map(({ name, description, color, handle }) => (
|
||||
<Card
|
||||
key={title}
|
||||
variant={cardVariant}
|
||||
key={name}
|
||||
variant={`gradient-${color}` as CardProps['variant']}
|
||||
className="flex flex-col justify-between"
|
||||
>
|
||||
<CardHeader className="items-end-safe">
|
||||
<CardHeader className="relative flex flex-row justify-between">
|
||||
<div
|
||||
className={cn(
|
||||
'flex size-8 items-center-safe justify-center-safe rounded-full text-white',
|
||||
iconBg,
|
||||
'flex size-8 items-center-safe justify-center-safe rounded-full',
|
||||
`text-${color}`,
|
||||
`bg-${color}/10`,
|
||||
{
|
||||
'bg-primary/10': color === 'success',
|
||||
},
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
<ComponentInstanceIcon
|
||||
className={cn('size-4', `fill-${color}`)}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute top-2 right-2 flex size-8 items-center-safe justify-center-safe rounded-xl text-white">
|
||||
<Link
|
||||
href={pathsConfig.app.bookingHandle.replace('[handle]', handle)}
|
||||
>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
className="px-2 text-black"
|
||||
>
|
||||
<ChevronRight className="size-4 stroke-2" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-col items-start gap-2">
|
||||
<div
|
||||
className={'flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-primary\/10 mb-6'}
|
||||
>
|
||||
<HeartPulse className="size-4 fill-green-500" />
|
||||
</div>
|
||||
<h5>
|
||||
<Trans i18nKey={title} />
|
||||
</h5>
|
||||
<CardDescription className={descriptionColor}>
|
||||
<Trans i18nKey={description} />
|
||||
</CardDescription>
|
||||
<CardFooter className="mt-5 flex flex-col items-start gap-2">
|
||||
<h5>{name}</h5>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
61
app/home/(user)/_components/service-categories.tsx
Normal file
61
app/home/(user)/_components/service-categories.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { createPath, pathsConfig } from '@/packages/shared/src/config';
|
||||
import { ComponentInstanceIcon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '@kit/ui/shadcn';
|
||||
import { Card, CardDescription, CardTitle } from '@kit/ui/shadcn/card';
|
||||
|
||||
export interface ServiceCategory {
|
||||
name: string;
|
||||
handle: string;
|
||||
color: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const ServiceCategories = ({
|
||||
categories,
|
||||
}: {
|
||||
categories: ServiceCategory[];
|
||||
}) => {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
|
||||
{categories.map((category, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="flex cursor-pointer gap-2 p-4 shadow hover:shadow-md"
|
||||
onClick={() => {
|
||||
redirect(
|
||||
pathsConfig.app.bookingHandle.replace(
|
||||
'[handle]',
|
||||
category.handle,
|
||||
),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'flex size-8 items-center-safe justify-center-safe rounded-full',
|
||||
`bg-${category.color}/10`,
|
||||
`text-${category.color}`,
|
||||
)}
|
||||
>
|
||||
<ComponentInstanceIcon />
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="mb-2 text-lg font-semibold">{category.name}</h5>
|
||||
<CardDescription className="">
|
||||
{category.description}
|
||||
</CardDescription>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceCategories;
|
||||
@@ -1,9 +1,11 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { listProductTypes } from "@lib/data/products";
|
||||
import { listRegions } from '@lib/data/regions';
|
||||
import { getProductCategories } from '@lib/data/categories';
|
||||
import { listProductTypes } from '@lib/data/products';
|
||||
import { listRegions } from '@lib/data/regions';
|
||||
|
||||
import { OrderAnalysisCard } from '../../_components/order-analyses-cards';
|
||||
import { ServiceCategory } from '../../_components/service-categories';
|
||||
|
||||
async function countryCodesLoader() {
|
||||
const countryCodes = await listRegions().then((regions) =>
|
||||
@@ -14,7 +16,9 @@ async function countryCodesLoader() {
|
||||
export const loadCountryCodes = cache(countryCodesLoader);
|
||||
|
||||
async function productCategoriesLoader() {
|
||||
const productCategories = await getProductCategories({ fields: "*products, *products.variants" });
|
||||
const productCategories = await getProductCategories({
|
||||
fields: '*products, *products.variants, is_active',
|
||||
});
|
||||
return productCategories.product_categories ?? [];
|
||||
}
|
||||
export const loadProductCategories = cache(productCategoriesLoader);
|
||||
@@ -29,25 +33,34 @@ async function analysesLoader() {
|
||||
const [countryCodes, productCategories] = await Promise.all([
|
||||
loadCountryCodes(),
|
||||
loadProductCategories(),
|
||||
]);
|
||||
]);
|
||||
const countryCode = countryCodes[0]!;
|
||||
|
||||
const category = productCategories.find(({ metadata }) => metadata?.page === 'order-analysis');
|
||||
const category = productCategories.find(
|
||||
({ metadata }) => metadata?.page === 'order-analysis',
|
||||
);
|
||||
const serviceCategories = productCategories.filter(
|
||||
({ parent_category }) => parent_category?.handle === 'tto-categories',
|
||||
);
|
||||
|
||||
return {
|
||||
analyses: category?.products?.map<OrderAnalysisCard>(({ title, description, subtitle, variants, status, metadata }) => {
|
||||
const variant = variants![0]!;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
subtitle,
|
||||
variant: {
|
||||
id: variant.id,
|
||||
analyses:
|
||||
category?.products?.map<OrderAnalysisCard>(
|
||||
({ title, description, subtitle, variants, status, metadata }) => {
|
||||
const variant = variants![0]!;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
subtitle,
|
||||
variant: {
|
||||
id: variant.id,
|
||||
},
|
||||
isAvailable:
|
||||
status === 'published' && !!metadata?.analysisIdOriginal,
|
||||
};
|
||||
},
|
||||
isAvailable: status === 'published' && !!metadata?.analysisIdOriginal,
|
||||
};
|
||||
}) ?? [],
|
||||
) ?? [],
|
||||
countryCode,
|
||||
}
|
||||
};
|
||||
}
|
||||
export const loadAnalyses = cache(analysesLoader);
|
||||
|
||||
31
app/home/(user)/_lib/server/load-category.ts
Normal file
31
app/home/(user)/_lib/server/load-category.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { getProductCategories } from '@lib/data';
|
||||
|
||||
import { ServiceCategory } from '../../_components/service-categories';
|
||||
|
||||
async function categoryLoader({
|
||||
handle,
|
||||
}: {
|
||||
handle: string;
|
||||
}): Promise<{ category: ServiceCategory | null }> {
|
||||
const response = await getProductCategories({
|
||||
handle,
|
||||
fields: '*products, is_active, metadata',
|
||||
});
|
||||
|
||||
const category = response.product_categories[0];
|
||||
|
||||
return {
|
||||
category: {
|
||||
color:
|
||||
typeof category?.metadata?.color === 'string'
|
||||
? category?.metadata?.color
|
||||
: 'primary',
|
||||
description: category?.description || '',
|
||||
handle: category?.handle || '',
|
||||
name: category?.name || '',
|
||||
},
|
||||
};
|
||||
}
|
||||
export const loadCategory = cache(categoryLoader);
|
||||
49
app/home/(user)/_lib/server/load-tto-services.ts
Normal file
49
app/home/(user)/_lib/server/load-tto-services.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { getProductCategories } from '@lib/data';
|
||||
|
||||
import { ServiceCategory } from '../../_components/service-categories';
|
||||
|
||||
async function ttoServicesLoader() {
|
||||
const response = await getProductCategories({
|
||||
fields: '*products, is_active, metadata',
|
||||
});
|
||||
|
||||
const heroCategories = response.product_categories?.filter(
|
||||
({ parent_category, is_active, metadata }) =>
|
||||
parent_category?.handle === 'tto-categories' &&
|
||||
is_active &&
|
||||
metadata?.isHero,
|
||||
);
|
||||
|
||||
const ttoCategories = response.product_categories?.filter(
|
||||
({ parent_category, is_active, metadata }) =>
|
||||
parent_category?.handle === 'tto-categories' &&
|
||||
is_active &&
|
||||
!metadata?.isHero,
|
||||
);
|
||||
|
||||
return {
|
||||
heroCategories:
|
||||
heroCategories.map<ServiceCategory>(
|
||||
({ name, handle, metadata, description }) => ({
|
||||
name,
|
||||
handle,
|
||||
color:
|
||||
typeof metadata?.color === 'string' ? metadata.color : 'primary',
|
||||
description,
|
||||
}),
|
||||
) ?? [],
|
||||
ttoCategories:
|
||||
ttoCategories.map<ServiceCategory>(
|
||||
({ name, handle, metadata, description }) => ({
|
||||
name,
|
||||
handle,
|
||||
color:
|
||||
typeof metadata?.color === 'string' ? metadata.color : 'primary',
|
||||
description,
|
||||
}),
|
||||
) ?? [],
|
||||
};
|
||||
}
|
||||
export const loadTtoServices = cache(ttoServicesLoader);
|
||||
@@ -1,13 +1,13 @@
|
||||
import { sdk } from "@lib/config"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { getCacheOptions } from "./cookies"
|
||||
import { sdk } from "@lib/config";
|
||||
import { HttpTypes } from "@medusajs/types";
|
||||
import { getCacheOptions } from "./cookies";
|
||||
|
||||
export const listCategories = async (query?: Record<string, any>) => {
|
||||
const next = {
|
||||
...(await getCacheOptions("categories")),
|
||||
}
|
||||
};
|
||||
|
||||
const limit = query?.limit || 100
|
||||
const limit = query?.limit || 100;
|
||||
|
||||
return sdk.client
|
||||
.fetch<{ product_categories: HttpTypes.StoreProductCategory[] }>(
|
||||
@@ -23,8 +23,8 @@ export const listCategories = async (query?: Record<string, any>) => {
|
||||
cache: "force-cache",
|
||||
}
|
||||
)
|
||||
.then(({ product_categories }) => product_categories)
|
||||
}
|
||||
.then(({ product_categories }) => product_categories);
|
||||
};
|
||||
|
||||
export const getCategoryByHandle = async (categoryHandle: string[]) => {
|
||||
const { product_categories } = await getProductCategories({
|
||||
@@ -32,7 +32,7 @@ export const getCategoryByHandle = async (categoryHandle: string[]) => {
|
||||
limit: 1,
|
||||
});
|
||||
return product_categories[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const getProductCategories = async ({
|
||||
handle,
|
||||
@@ -45,19 +45,18 @@ export const getProductCategories = async ({
|
||||
} = {}) => {
|
||||
const next = {
|
||||
...(await getCacheOptions("categories")),
|
||||
}
|
||||
};
|
||||
|
||||
return sdk.client
|
||||
.fetch<HttpTypes.StoreProductCategoryListResponse>(
|
||||
`/store/product-categories`,
|
||||
{
|
||||
query: {
|
||||
fields,
|
||||
handle,
|
||||
limit,
|
||||
},
|
||||
next,
|
||||
//cache: "force-cache",
|
||||
}
|
||||
);
|
||||
}
|
||||
return sdk.client.fetch<HttpTypes.StoreProductCategoryListResponse>(
|
||||
`/store/product-categories`,
|
||||
{
|
||||
query: {
|
||||
fields,
|
||||
handle,
|
||||
limit,
|
||||
},
|
||||
next,
|
||||
//cache: "force-cache",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ const PathsSchema = z.object({
|
||||
home: z.string().min(1),
|
||||
selectPackage: z.string().min(1),
|
||||
booking: z.string().min(1),
|
||||
bookingHandle: z.string().min(1),
|
||||
myOrders: z.string().min(1),
|
||||
analysisResults: z.string().min(1),
|
||||
orderAnalysisPackage: z.string().min(1),
|
||||
@@ -64,6 +65,7 @@ const pathsConfig = PathsSchema.parse({
|
||||
joinTeam: '/join',
|
||||
selectPackage: '/select-package',
|
||||
booking: '/home/booking',
|
||||
bookingHandle: '/home/booking/[handle]',
|
||||
orderAnalysisPackage: '/home/order-analysis-package',
|
||||
myOrders: '/home/order',
|
||||
analysisResults: '/home/analysis-results',
|
||||
|
||||
@@ -1088,7 +1088,7 @@ export type Database = {
|
||||
price: number
|
||||
price_periods: string | null
|
||||
requires_payment: boolean
|
||||
sync_id: number
|
||||
sync_id: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
@@ -1107,7 +1107,7 @@ export type Database = {
|
||||
price: number
|
||||
price_periods?: string | null
|
||||
requires_payment: boolean
|
||||
sync_id: number
|
||||
sync_id?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
@@ -1126,7 +1126,7 @@ export type Database = {
|
||||
price?: number
|
||||
price_periods?: string | null
|
||||
requires_payment?: boolean
|
||||
sync_id?: number
|
||||
sync_id?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
@@ -1147,7 +1147,7 @@ export type Database = {
|
||||
doctor_user_id: string | null
|
||||
id: number
|
||||
status: Database["medreport"]["Enums"]["analysis_feedback_status"]
|
||||
updated_at: string
|
||||
updated_at: string | null
|
||||
updated_by: string | null
|
||||
user_id: string
|
||||
value: string | null
|
||||
@@ -1159,7 +1159,7 @@ export type Database = {
|
||||
doctor_user_id?: string | null
|
||||
id?: number
|
||||
status?: Database["medreport"]["Enums"]["analysis_feedback_status"]
|
||||
updated_at?: string
|
||||
updated_at?: string | null
|
||||
updated_by?: string | null
|
||||
user_id: string
|
||||
value?: string | null
|
||||
@@ -1171,7 +1171,7 @@ export type Database = {
|
||||
doctor_user_id?: string | null
|
||||
id?: number
|
||||
status?: Database["medreport"]["Enums"]["analysis_feedback_status"]
|
||||
updated_at?: string
|
||||
updated_at?: string | null
|
||||
updated_by?: string | null
|
||||
user_id?: string
|
||||
value?: string | null
|
||||
@@ -1850,9 +1850,7 @@ export type Database = {
|
||||
Returns: Json
|
||||
}
|
||||
create_team_account: {
|
||||
Args:
|
||||
| { account_name: string }
|
||||
| { account_name: string; new_personal_code: string }
|
||||
Args: { account_name: string; new_personal_code: string }
|
||||
Returns: {
|
||||
application_role: Database["medreport"]["Enums"]["application_role"]
|
||||
city: string | null
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"title": "Vali teenus",
|
||||
"description": "Vali sobiv teenus või pakett vastavalt oma tervisemurele või -eesmärgile.",
|
||||
"analysisPackages": {
|
||||
"title": "Analüüside paketid",
|
||||
"description": "Tutvu personaalsete analüüsi pakettidega ja telli"
|
||||
}
|
||||
}
|
||||
"title": "Vali teenus",
|
||||
"description": "Vali sobiv teenus või pakett vastavalt oma tervisemurele või -eesmärgile.",
|
||||
"analysisPackages": {
|
||||
"title": "Analüüside paketid",
|
||||
"description": "Tutvu personaalsete analüüsi pakettidega ja telli"
|
||||
},
|
||||
"noCategories": "Teenuste loetelu ei leitud, proovi hiljem uuesti"
|
||||
}
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
"dashboard": "Ülevaade",
|
||||
"settings": "Settings",
|
||||
"profile": "Profile",
|
||||
"application": "Application"
|
||||
"application": "Application",
|
||||
"pickTime": "Vali aeg"
|
||||
},
|
||||
"roles": {
|
||||
"owner": {
|
||||
|
||||
2
supabase/migrations/20250827134000_bookings.sql
Normal file
2
supabase/migrations/20250827134000_bookings.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE medreport.connected_online_services
|
||||
ALTER COLUMN sync_id TYPE text USING sync_id::text;
|
||||
Reference in New Issue
Block a user