4 Commits

Author SHA1 Message Date
bc61db07b2 test2 2025-09-04 01:21:42 +03:00
3d58e5aa84 try to display price before adding to cart 2025-09-03 23:04:09 +03:00
8ecca096f2 fix adding to cart loading 2025-09-03 23:03:00 +03:00
84c8dcc792 env test 2025-09-03 14:24:56 +03:00
13 changed files with 119 additions and 19 deletions

2
.env
View File

@@ -13,7 +13,7 @@ NEXT_PUBLIC_THEME_COLOR="#ffffff"
NEXT_PUBLIC_THEME_COLOR_DARK="#0a0a0a"
# AUTH
NEXT_PUBLIC_AUTH_PASSWORD=true
NEXT_PUBLIC_AUTH_PASSWORD=false
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
NEXT_PUBLIC_CAPTCHA_SITE_KEY=

View File

@@ -36,3 +36,57 @@ MONTONIO_API_URL=https://sandbox-stargate.montonio.com
# JOBS
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

View File

@@ -20,3 +20,5 @@ EMAIL_PASSWORD=password
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51K9cWKI1i3VnbZTq2HGstY2S8wt3peF1MOqPXFO4LR8ln2QgS7GxL8XyKaKLvn7iFHeqAnvdDw0o48qN7rrwwcHU00jOtKhjsf
CONTACT_EMAIL=test@makerkit.dev
SUPABASE_AUTH_KEYCLOAK_URL=https://keycloak.medreport.ee/realms/medreport-prod

View File

@@ -1,6 +1,7 @@
"use client";
import { HeartPulse, Loader2, ShoppingCart } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Button } from '@kit/ui/button';
import {
@@ -15,12 +16,14 @@ import { handleAddToCart } from '~/lib/services/medusaCart.service';
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
import { Trans } from '@kit/ui/trans';
import { toast } from '@kit/ui/sonner';
import { formatCurrency } from '@/packages/shared/src/utils';
export type OrderAnalysisCard = Pick<
StoreProduct, 'title' | 'description' | 'subtitle'
> & {
isAvailable: boolean;
variant: { id: string };
price: number | null;
};
export default function OrderAnalysesCards({
@@ -30,23 +33,26 @@ export default function OrderAnalysesCards({
analyses: OrderAnalysisCard[];
countryCode: string;
}) {
const [isAddingToCart, setIsAddingToCart] = useState(false);
const { i18n: { language } } = useTranslation()
const [variantAddingToCart, setVariantAddingToCart] = useState<string | null>(null);
const handleSelect = async (variantId: string) => {
if (isAddingToCart) {
if (variantAddingToCart) {
return null;
}
setIsAddingToCart(true);
setVariantAddingToCart(variantId);
try {
await handleAddToCart({
selectedVariant: { id: variantId },
countryCode,
});
toast.success(<Trans i18nKey={'order-analysis:analysisAddedToCart'} />);
setIsAddingToCart(false);
setVariantAddingToCart(null);
} catch (e) {
toast.error(<Trans i18nKey={'order-analysis:analysisAddToCartError'} />);
setIsAddingToCart(false);
setVariantAddingToCart(null);
console.error(e);
}
}
@@ -59,7 +65,15 @@ export default function OrderAnalysesCards({
description,
subtitle,
isAvailable,
price,
}) => {
const formattedPrice = typeof price === 'number'
? formatCurrency({
currencyCode: 'eur',
locale: language,
value: price,
})
: null;
return (
<Card
key={title}
@@ -80,7 +94,7 @@ export default function OrderAnalysesCards({
className="px-2 text-black"
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>
</div>
)}
@@ -91,7 +105,14 @@ export default function OrderAnalysesCards({
{description && (
<>
{' '}
<InfoTooltip content={`${description}`} />
<InfoTooltip
content={
<div className='flex flex-col gap-2'>
<span>{formattedPrice}</span>
<span>{description}</span>
</div>
}
/>
</>
)}
</h5>

View File

@@ -1,11 +1,10 @@
import { cache } from 'react';
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 { OrderAnalysisCard } from '../../_components/order-analyses-cards';
import { ServiceCategory } from '../../_components/service-categories';
async function countryCodesLoader() {
const countryCodes = await listRegions().then((regions) =>
@@ -39,13 +38,20 @@ async function analysesLoader() {
const category = productCategories.find(
({ metadata }) => metadata?.page === 'order-analysis',
);
const categoryProducts = category
? await listProducts({
countryCode,
queryParams: { limit: 100, category_id: category.id },
})
: null;
const serviceCategories = productCategories.filter(
({ parent_category }) => parent_category?.handle === 'tto-categories',
);
return {
analyses:
category?.products?.map<OrderAnalysisCard>(
categoryProducts?.response.products.map<OrderAnalysisCard>(
({ title, description, subtitle, variants, status, metadata }) => {
const variant = variants![0]!;
return {
@@ -57,6 +63,7 @@ async function analysesLoader() {
},
isAvailable:
status === 'published' && !!metadata?.analysisIdOriginal,
price: variant.calculated_price?.calculated_amount ?? null,
};
},
) ?? [],

View File

@@ -5,7 +5,6 @@ import { Database } from '@kit/supabase/database';
import {
AnalysisResultDetails,
UserAnalysis,
UserAnalysisResponse,
} from '../types/accounts';
export type AccountWithParams =

View File

@@ -102,6 +102,7 @@ export const OauthProviders: React.FC<{
redirectTo,
queryParams: props.queryParams,
scopes,
skipBrowserRedirect: false,
},
} satisfies SignInWithOAuthCredentials;

View File

@@ -14,7 +14,7 @@ export const listProducts = async ({
regionId,
}: {
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
regionId?: string
}): Promise<{
@@ -63,7 +63,7 @@ export const listProducts = async ({
offset,
region_id: region?.id,
fields:
"*variants.calculated_price,+variants.inventory_quantity,+metadata,+tags",
"*variants.calculated_price,+variants.inventory_quantity,+metadata,+tags,+status",
...queryParams,
},
headers,

View File

@@ -13,7 +13,7 @@ export function InfoTooltip({
content,
icon,
}: {
content?: string | null;
content?: JSX.Element | string | null;
icon?: JSX.Element;
}) {
if (!content) return null;
@@ -23,7 +23,7 @@ export function InfoTooltip({
<TooltipTrigger>
{icon || <Info className="size-4 cursor-pointer" />}
</TooltipTrigger>
<TooltipContent>{content}</TooltipContent>
<TooltipContent className='sm:max-w-[400px]'>{content}</TooltipContent>
</Tooltip>
</TooltipProvider>
);

View File

@@ -40,7 +40,7 @@ const authConfig = AuthConfigSchema.parse({
providers: {
password: process.env.NEXT_PUBLIC_AUTH_PASSWORD === 'true',
magicLink: process.env.NEXT_PUBLIC_AUTH_MAGIC_LINK === 'true',
oAuth: ['google'],
oAuth: ['keycloak'],
},
} satisfies z.infer<typeof AuthConfigSchema>);

View File

@@ -10,5 +10,11 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
export function getSupabaseBrowserClient<GenericSchema = Database>() {
const keys = getSupabaseClientKeys();
return createBrowserClient<GenericSchema>(keys.url, keys.anonKey);
return createBrowserClient<GenericSchema>(keys.url, keys.anonKey, {
auth: {
flowType: 'pkce',
autoRefreshToken: true,
persistSession: true,
},
});
}

View File

@@ -20,6 +20,11 @@ export function createMiddlewareClient<GenericSchema = Database>(
const keys = getSupabaseClientKeys();
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
auth: {
flowType: 'pkce',
autoRefreshToken: true,
persistSession: true,
},
cookies: {
getAll() {
return request.cookies.getAll();

View File

@@ -15,6 +15,11 @@ export function getSupabaseServerClient<GenericSchema = Database>() {
const keys = getSupabaseClientKeys();
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
auth: {
flowType: 'pkce',
autoRefreshToken: true,
persistSession: true,
},
cookies: {
async getAll() {
const cookieStore = await cookies();