Compare commits
4 Commits
1b17dd845a
...
keycloak
| Author | SHA1 | Date | |
|---|---|---|---|
| bc61db07b2 | |||
| 3d58e5aa84 | |||
| 8ecca096f2 | |||
| 84c8dcc792 |
2
.env
2
.env
@@ -13,7 +13,7 @@ NEXT_PUBLIC_THEME_COLOR="#ffffff"
|
|||||||
NEXT_PUBLIC_THEME_COLOR_DARK="#0a0a0a"
|
NEXT_PUBLIC_THEME_COLOR_DARK="#0a0a0a"
|
||||||
|
|
||||||
# AUTH
|
# AUTH
|
||||||
NEXT_PUBLIC_AUTH_PASSWORD=true
|
NEXT_PUBLIC_AUTH_PASSWORD=false
|
||||||
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
|
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
|
||||||
NEXT_PUBLIC_CAPTCHA_SITE_KEY=
|
NEXT_PUBLIC_CAPTCHA_SITE_KEY=
|
||||||
|
|
||||||
|
|||||||
@@ -36,3 +36,57 @@ MONTONIO_API_URL=https://sandbox-stargate.montonio.com
|
|||||||
|
|
||||||
# JOBS
|
# JOBS
|
||||||
JOBS_API_TOKEN=73ce073c-6dd4-11f0-8e75-8fee89786197
|
JOBS_API_TOKEN=73ce073c-6dd4-11f0-8e75-8fee89786197
|
||||||
|
|
||||||
|
# MEDIPOST
|
||||||
|
|
||||||
|
MEDIPOST_URL=https://meditest.medisoft.ee:7443/Medipost/MedipostServlet
|
||||||
|
MEDIPOST_USER=trvurgtst
|
||||||
|
MEDIPOST_PASSWORD=SRB48HZMV
|
||||||
|
MEDIPOST_RECIPIENT=trvurgtst
|
||||||
|
MEDIPOST_MESSAGE_SENDER=trvurgtst
|
||||||
|
|
||||||
|
MEDIPOST_URL=https://medipost2.medisoft.ee:8443/Medipost/MedipostServlet
|
||||||
|
MEDIPOST_USER=medreport
|
||||||
|
MEDIPOST_PASSWORD=85MXFFDB7
|
||||||
|
MEDIPOST_RECIPIENT=HTI
|
||||||
|
MEDIPOST_MESSAGE_SENDER=medreport
|
||||||
|
|
||||||
|
### TEST.MEDREPORT.ee ###
|
||||||
|
|
||||||
|
DB_PASSWORD=T#u-$M7%RjbA@L@
|
||||||
|
|
||||||
|
#### MEDUSA
|
||||||
|
MEDUSA_BACKEND_URL=https://backoffice-test.medreport.ee
|
||||||
|
MEDUSA_BACKEND_PUBLIC_URL=https://backoffice-test.medreport.ee
|
||||||
|
MEDUSA_SECRET_API_KEY=sk_fdb1808fbabf62979cc46316aa997378ffbb87882883e8f5c3ee47cee39dcac5
|
||||||
|
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_827a2ab863021cb67993f1d81078f81bfce4b4e0da642d8c0f5398ded9d8fd32
|
||||||
|
|
||||||
|
#### SUPABASE
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL=https://oqsdacktkhmbylmzstjq.supabase.co
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9xc2RhY2t0a2htYnlsbXpzdGpxIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDY1MjgxMjMsImV4cCI6MjA2MjEwNDEyM30.LdHCTWxijFmhXdnT9KVuLRAVbtSwY7OO-oLtpd8GmO0
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9xc2RhY2t0a2htYnlsbXpzdGpxIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0NjUyODEyMywiZXhwIjoyMDYyMTA0MTIzfQ.KVcnkZ21Pd0XkJho23dZqFHawVTLQqfvF7l2RxsELLk
|
||||||
|
|
||||||
|
#######
|
||||||
|
|
||||||
|
### LOCAL ###
|
||||||
|
|
||||||
|
#### MEDUSA
|
||||||
|
MEDUSA_BACKEND_URL=http://localhost:9000
|
||||||
|
MEDUSA_BACKEND_PUBLIC_URL=http://localhost:9000
|
||||||
|
#MEDUSA_BACKEND_URL=http://5.181.51.38:9000
|
||||||
|
#MEDUSA_BACKEND_PUBLIC_URL=http://5.181.51.38:9000
|
||||||
|
MEDUSA_SECRET_API_KEY=sk_b332d525212ab4078ef73fb2b8232c3beebccc4a460e2c7abf6e187a458d60cf
|
||||||
|
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_e23a820689a07d55aa0a0ad187268559f5d6288ecb0768ff4520516285bdef84
|
||||||
|
#NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_0ec86252438b38ce18d5601f7877e4395d7e0a6afa8687dfea8d37af33015633
|
||||||
|
|
||||||
|
#### SUPABASE
|
||||||
|
NEXT_PUBLIC_SUPABASE_URL=http://5.181.51.38:54321
|
||||||
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
|
||||||
|
|
||||||
|
#######
|
||||||
|
|
||||||
|
SUPABASE_AUTH_CLIENT_ID=supabase
|
||||||
|
SUPABASE_AUTH_KEYCLOAK_SECRET=Gl394GjizClhQl06KFeoFyZ7ZbPamG5I
|
||||||
|
SUPABASE_AUTH_KEYCLOAK_URL=http://localhost:8585/realms/medreport-sandbox
|
||||||
|
SUPABASE_AUTH_KEYCLOAK_CALLBACK_URL=http://localhost:3000/auth/callback
|
||||||
|
|||||||
@@ -19,4 +19,6 @@ EMAIL_PASSWORD=password
|
|||||||
# STRIPE
|
# STRIPE
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51K9cWKI1i3VnbZTq2HGstY2S8wt3peF1MOqPXFO4LR8ln2QgS7GxL8XyKaKLvn7iFHeqAnvdDw0o48qN7rrwwcHU00jOtKhjsf
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51K9cWKI1i3VnbZTq2HGstY2S8wt3peF1MOqPXFO4LR8ln2QgS7GxL8XyKaKLvn7iFHeqAnvdDw0o48qN7rrwwcHU00jOtKhjsf
|
||||||
|
|
||||||
CONTACT_EMAIL=test@makerkit.dev
|
CONTACT_EMAIL=test@makerkit.dev
|
||||||
|
|
||||||
|
SUPABASE_AUTH_KEYCLOAK_URL=https://keycloak.medreport.ee/realms/medreport-prod
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { HeartPulse, Loader2, ShoppingCart } from 'lucide-react';
|
import { HeartPulse, Loader2, ShoppingCart } from 'lucide-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Button } from '@kit/ui/button';
|
import { Button } from '@kit/ui/button';
|
||||||
import {
|
import {
|
||||||
@@ -15,12 +16,14 @@ import { handleAddToCart } from '~/lib/services/medusaCart.service';
|
|||||||
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
import { toast } from '@kit/ui/sonner';
|
import { toast } from '@kit/ui/sonner';
|
||||||
|
import { formatCurrency } from '@/packages/shared/src/utils';
|
||||||
|
|
||||||
export type OrderAnalysisCard = Pick<
|
export type OrderAnalysisCard = Pick<
|
||||||
StoreProduct, 'title' | 'description' | 'subtitle'
|
StoreProduct, 'title' | 'description' | 'subtitle'
|
||||||
> & {
|
> & {
|
||||||
isAvailable: boolean;
|
isAvailable: boolean;
|
||||||
variant: { id: string };
|
variant: { id: string };
|
||||||
|
price: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function OrderAnalysesCards({
|
export default function OrderAnalysesCards({
|
||||||
@@ -30,23 +33,26 @@ export default function OrderAnalysesCards({
|
|||||||
analyses: OrderAnalysisCard[];
|
analyses: OrderAnalysisCard[];
|
||||||
countryCode: string;
|
countryCode: string;
|
||||||
}) {
|
}) {
|
||||||
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
|
||||||
|
const { i18n: { language } } = useTranslation()
|
||||||
|
|
||||||
|
const [variantAddingToCart, setVariantAddingToCart] = useState<string | null>(null);
|
||||||
const handleSelect = async (variantId: string) => {
|
const handleSelect = async (variantId: string) => {
|
||||||
if (isAddingToCart) {
|
if (variantAddingToCart) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsAddingToCart(true);
|
setVariantAddingToCart(variantId);
|
||||||
try {
|
try {
|
||||||
await handleAddToCart({
|
await handleAddToCart({
|
||||||
selectedVariant: { id: variantId },
|
selectedVariant: { id: variantId },
|
||||||
countryCode,
|
countryCode,
|
||||||
});
|
});
|
||||||
toast.success(<Trans i18nKey={'order-analysis:analysisAddedToCart'} />);
|
toast.success(<Trans i18nKey={'order-analysis:analysisAddedToCart'} />);
|
||||||
setIsAddingToCart(false);
|
setVariantAddingToCart(null);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(<Trans i18nKey={'order-analysis:analysisAddToCartError'} />);
|
toast.error(<Trans i18nKey={'order-analysis:analysisAddToCartError'} />);
|
||||||
setIsAddingToCart(false);
|
setVariantAddingToCart(null);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +65,15 @@ export default function OrderAnalysesCards({
|
|||||||
description,
|
description,
|
||||||
subtitle,
|
subtitle,
|
||||||
isAvailable,
|
isAvailable,
|
||||||
|
price,
|
||||||
}) => {
|
}) => {
|
||||||
|
const formattedPrice = typeof price === 'number'
|
||||||
|
? formatCurrency({
|
||||||
|
currencyCode: 'eur',
|
||||||
|
locale: language,
|
||||||
|
value: price,
|
||||||
|
})
|
||||||
|
: null;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={title}
|
key={title}
|
||||||
@@ -80,7 +94,7 @@ export default function OrderAnalysesCards({
|
|||||||
className="px-2 text-black"
|
className="px-2 text-black"
|
||||||
onClick={() => handleSelect(variant.id)}
|
onClick={() => handleSelect(variant.id)}
|
||||||
>
|
>
|
||||||
{isAddingToCart ? <Loader2 className="size-4 stroke-2 animate-spin" /> : <ShoppingCart className="size-4 stroke-2" />}
|
{variantAddingToCart ? <Loader2 className="size-4 stroke-2 animate-spin" /> : <ShoppingCart className="size-4 stroke-2" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -91,7 +105,14 @@ export default function OrderAnalysesCards({
|
|||||||
{description && (
|
{description && (
|
||||||
<>
|
<>
|
||||||
{' '}
|
{' '}
|
||||||
<InfoTooltip content={`${description}`} />
|
<InfoTooltip
|
||||||
|
content={
|
||||||
|
<div className='flex flex-col gap-2'>
|
||||||
|
<span>{formattedPrice}</span>
|
||||||
|
<span>{description}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</h5>
|
</h5>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { cache } from 'react';
|
import { cache } from 'react';
|
||||||
|
|
||||||
import { getProductCategories } from '@lib/data/categories';
|
import { getProductCategories } from '@lib/data/categories';
|
||||||
import { listProductTypes } from '@lib/data/products';
|
import { listProducts, listProductTypes } from '@lib/data/products';
|
||||||
import { listRegions } from '@lib/data/regions';
|
import { listRegions } from '@lib/data/regions';
|
||||||
|
|
||||||
import { OrderAnalysisCard } from '../../_components/order-analyses-cards';
|
import { OrderAnalysisCard } from '../../_components/order-analyses-cards';
|
||||||
import { ServiceCategory } from '../../_components/service-categories';
|
|
||||||
|
|
||||||
async function countryCodesLoader() {
|
async function countryCodesLoader() {
|
||||||
const countryCodes = await listRegions().then((regions) =>
|
const countryCodes = await listRegions().then((regions) =>
|
||||||
@@ -39,13 +38,20 @@ async function analysesLoader() {
|
|||||||
const category = productCategories.find(
|
const category = productCategories.find(
|
||||||
({ metadata }) => metadata?.page === 'order-analysis',
|
({ metadata }) => metadata?.page === 'order-analysis',
|
||||||
);
|
);
|
||||||
|
const categoryProducts = category
|
||||||
|
? await listProducts({
|
||||||
|
countryCode,
|
||||||
|
queryParams: { limit: 100, category_id: category.id },
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
const serviceCategories = productCategories.filter(
|
const serviceCategories = productCategories.filter(
|
||||||
({ parent_category }) => parent_category?.handle === 'tto-categories',
|
({ parent_category }) => parent_category?.handle === 'tto-categories',
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
analyses:
|
analyses:
|
||||||
category?.products?.map<OrderAnalysisCard>(
|
categoryProducts?.response.products.map<OrderAnalysisCard>(
|
||||||
({ title, description, subtitle, variants, status, metadata }) => {
|
({ title, description, subtitle, variants, status, metadata }) => {
|
||||||
const variant = variants![0]!;
|
const variant = variants![0]!;
|
||||||
return {
|
return {
|
||||||
@@ -57,6 +63,7 @@ async function analysesLoader() {
|
|||||||
},
|
},
|
||||||
isAvailable:
|
isAvailable:
|
||||||
status === 'published' && !!metadata?.analysisIdOriginal,
|
status === 'published' && !!metadata?.analysisIdOriginal,
|
||||||
|
price: variant.calculated_price?.calculated_amount ?? null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
) ?? [],
|
) ?? [],
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Database } from '@kit/supabase/database';
|
|||||||
import {
|
import {
|
||||||
AnalysisResultDetails,
|
AnalysisResultDetails,
|
||||||
UserAnalysis,
|
UserAnalysis,
|
||||||
UserAnalysisResponse,
|
|
||||||
} from '../types/accounts';
|
} from '../types/accounts';
|
||||||
|
|
||||||
export type AccountWithParams =
|
export type AccountWithParams =
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export const OauthProviders: React.FC<{
|
|||||||
redirectTo,
|
redirectTo,
|
||||||
queryParams: props.queryParams,
|
queryParams: props.queryParams,
|
||||||
scopes,
|
scopes,
|
||||||
|
skipBrowserRedirect: false,
|
||||||
},
|
},
|
||||||
} satisfies SignInWithOAuthCredentials;
|
} satisfies SignInWithOAuthCredentials;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const listProducts = async ({
|
|||||||
regionId,
|
regionId,
|
||||||
}: {
|
}: {
|
||||||
pageParam?: number
|
pageParam?: number
|
||||||
queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams & { "type_id[0]"?: string; id?: string[] }
|
queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams & { "type_id[0]"?: string; id?: string[], category_id?: string }
|
||||||
countryCode?: string
|
countryCode?: string
|
||||||
regionId?: string
|
regionId?: string
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
@@ -63,7 +63,7 @@ export const listProducts = async ({
|
|||||||
offset,
|
offset,
|
||||||
region_id: region?.id,
|
region_id: region?.id,
|
||||||
fields:
|
fields:
|
||||||
"*variants.calculated_price,+variants.inventory_quantity,+metadata,+tags",
|
"*variants.calculated_price,+variants.inventory_quantity,+metadata,+tags,+status",
|
||||||
...queryParams,
|
...queryParams,
|
||||||
},
|
},
|
||||||
headers,
|
headers,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function InfoTooltip({
|
|||||||
content,
|
content,
|
||||||
icon,
|
icon,
|
||||||
}: {
|
}: {
|
||||||
content?: string | null;
|
content?: JSX.Element | string | null;
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
}) {
|
}) {
|
||||||
if (!content) return null;
|
if (!content) return null;
|
||||||
@@ -23,7 +23,7 @@ export function InfoTooltip({
|
|||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
{icon || <Info className="size-4 cursor-pointer" />}
|
{icon || <Info className="size-4 cursor-pointer" />}
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>{content}</TooltipContent>
|
<TooltipContent className='sm:max-w-[400px]'>{content}</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const authConfig = AuthConfigSchema.parse({
|
|||||||
providers: {
|
providers: {
|
||||||
password: process.env.NEXT_PUBLIC_AUTH_PASSWORD === 'true',
|
password: process.env.NEXT_PUBLIC_AUTH_PASSWORD === 'true',
|
||||||
magicLink: process.env.NEXT_PUBLIC_AUTH_MAGIC_LINK === 'true',
|
magicLink: process.env.NEXT_PUBLIC_AUTH_MAGIC_LINK === 'true',
|
||||||
oAuth: ['google'],
|
oAuth: ['keycloak'],
|
||||||
},
|
},
|
||||||
} satisfies z.infer<typeof AuthConfigSchema>);
|
} satisfies z.infer<typeof AuthConfigSchema>);
|
||||||
|
|
||||||
|
|||||||
@@ -10,5 +10,11 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
|
|||||||
export function getSupabaseBrowserClient<GenericSchema = Database>() {
|
export function getSupabaseBrowserClient<GenericSchema = Database>() {
|
||||||
const keys = getSupabaseClientKeys();
|
const keys = getSupabaseClientKeys();
|
||||||
|
|
||||||
return createBrowserClient<GenericSchema>(keys.url, keys.anonKey);
|
return createBrowserClient<GenericSchema>(keys.url, keys.anonKey, {
|
||||||
|
auth: {
|
||||||
|
flowType: 'pkce',
|
||||||
|
autoRefreshToken: true,
|
||||||
|
persistSession: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ export function createMiddlewareClient<GenericSchema = Database>(
|
|||||||
const keys = getSupabaseClientKeys();
|
const keys = getSupabaseClientKeys();
|
||||||
|
|
||||||
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
|
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
|
||||||
|
auth: {
|
||||||
|
flowType: 'pkce',
|
||||||
|
autoRefreshToken: true,
|
||||||
|
persistSession: true,
|
||||||
|
},
|
||||||
cookies: {
|
cookies: {
|
||||||
getAll() {
|
getAll() {
|
||||||
return request.cookies.getAll();
|
return request.cookies.getAll();
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ export function getSupabaseServerClient<GenericSchema = Database>() {
|
|||||||
const keys = getSupabaseClientKeys();
|
const keys = getSupabaseClientKeys();
|
||||||
|
|
||||||
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
|
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
|
||||||
|
auth: {
|
||||||
|
flowType: 'pkce',
|
||||||
|
autoRefreshToken: true,
|
||||||
|
persistSession: true,
|
||||||
|
},
|
||||||
cookies: {
|
cookies: {
|
||||||
async getAll() {
|
async getAll() {
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
|
|||||||
Reference in New Issue
Block a user