Compare commits
4 Commits
5ef7f58f5d
...
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"
|
||||
|
||||
# AUTH
|
||||
NEXT_PUBLIC_AUTH_PASSWORD=true
|
||||
NEXT_PUBLIC_AUTH_PASSWORD=false
|
||||
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
|
||||
NEXT_PUBLIC_CAPTCHA_SITE_KEY=
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,4 +19,6 @@ EMAIL_PASSWORD=password
|
||||
# STRIPE
|
||||
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";
|
||||
|
||||
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>
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
},
|
||||
) ?? [],
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Database } from '@kit/supabase/database';
|
||||
import {
|
||||
AnalysisResultDetails,
|
||||
UserAnalysis,
|
||||
UserAnalysisResponse,
|
||||
} from '../types/accounts';
|
||||
|
||||
export type AccountWithParams =
|
||||
|
||||
@@ -102,6 +102,7 @@ export const OauthProviders: React.FC<{
|
||||
redirectTo,
|
||||
queryParams: props.queryParams,
|
||||
scopes,
|
||||
skipBrowserRedirect: false,
|
||||
},
|
||||
} satisfies SignInWithOAuthCredentials;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>);
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user