Compare commits
No commits in common. "f0d78ea01e673ab87c673d32417f1e891548f1c6" and "7046c5adc99ecf90a0ae89e1e4cdeae35a8a74fb" have entirely different histories.
f0d78ea01e
...
7046c5adc9
@ -14,21 +14,14 @@
|
|||||||
"@heroicons/react": "^2.1.5",
|
"@heroicons/react": "^2.1.5",
|
||||||
"@radix-ui/react-icons": "^1.3.1",
|
"@radix-ui/react-icons": "^1.3.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"accept-language": "^3.0.20",
|
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"fp-ts": "^2.16.9",
|
"fp-ts": "^2.16.9",
|
||||||
"i18next": "^23.16.4",
|
|
||||||
"i18next-browser-languagedetector": "^8.0.0",
|
|
||||||
"i18next-resources-to-backend": "^1.2.1",
|
|
||||||
"lucide-react": "^0.454.0",
|
"lucide-react": "^0.454.0",
|
||||||
"next": "15.0.2",
|
"next": "15.0.2",
|
||||||
"next-i18n-router": "^5.5.1",
|
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
"react": "19.0.0-rc-69d4b800-20241021",
|
"react": "19.0.0-rc-69d4b800-20241021",
|
||||||
"react-cookie": "^7.2.2",
|
|
||||||
"react-dom": "19.0.0-rc-69d4b800-20241021",
|
"react-dom": "19.0.0-rc-69d4b800-20241021",
|
||||||
"react-i18next": "^15.1.0",
|
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"tailwind-merge": "^2.5.4",
|
"tailwind-merge": "^2.5.4",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import DashboardSkeleton from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
|
|
||||||
|
|
||||||
export default function Loading() {
|
|
||||||
return <DashboardSkeleton />;
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
|
|
||||||
import CardWrapper from "@/app/[lang]/dashboard/components/server/cards/cards";
|
|
||||||
import LatestInvoices from "@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices";
|
|
||||||
import RevenueChart from "@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart";
|
|
||||||
import { Suspense } from "react";
|
|
||||||
import { getServerTranslation } from "@/bootstrap/i18n/i18n";
|
|
||||||
import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
|
|
||||||
|
|
||||||
export default async function Dashboard(props: {params: Promise<{lang: string}>}) {
|
|
||||||
const {lang} = await props.params
|
|
||||||
const { t } = await getServerTranslation(lang)
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<h1 className={`mb-4 text-xl md:text-2xl`}>
|
|
||||||
{t(langKey.global.dashboard)}
|
|
||||||
</h1>
|
|
||||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
||||||
<CardWrapper />
|
|
||||||
</div>
|
|
||||||
<div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
|
|
||||||
<Suspense fallback={<RevenueChartSkeleton />}>
|
|
||||||
<RevenueChart />
|
|
||||||
</Suspense>
|
|
||||||
<Suspense fallback={<LatestInvoicesSkeleton />}>
|
|
||||||
<LatestInvoices />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
import { initI18next } from "@/bootstrap/i18n/i18n";
|
|
||||||
import TranslationsProvider from "@/bootstrap/i18n/i18n-provider";
|
|
||||||
import { PropsWithChildren } from "react";
|
|
||||||
|
|
||||||
export default async function layout(props: PropsWithChildren & {params: Promise<{lang: string}>}) {
|
|
||||||
const lang = (await props.params).lang
|
|
||||||
const { resources} = await initI18next({lng: lang})
|
|
||||||
return <TranslationsProvider lng={lang} resources={resources}>{props.children}</TranslationsProvider>
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import Button from "@/app/components/button/button"
|
import Button from "@/app/components/button/button"
|
||||||
import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm"
|
import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm"
|
||||||
import { useDI } from "@/bootstrap/di/di-context"
|
import { useDI } from "@/bootstrap/di/di-context"
|
||||||
import { useRef } from "react"
|
import { useRef } from "react"
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import navLinkPersonalVM from '@/app/[lang]/dashboard/components/client/nav-links/nav-link-controller';
|
import navLinkPersonalVM from '@/app/dashboard/components/client/nav-links/nav-link-controller';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
import cardController from "@/app/[lang]/dashboard/components/server/card/card-controller";
|
import cardController from "@/app/dashboard/components/server/card/card-controller";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import { Card } from '@/app/[lang]/dashboard/components/server/card/card';
|
import { Card } from '@/app/dashboard/components/server/card/card';
|
||||||
import cardsController from '@/app/[lang]/dashboard/components/server/cards/cards-controller';
|
import cardsController from '@/app/dashboard/components/server/cards/cards-controller';
|
||||||
|
|
||||||
|
|
||||||
export default async function CardWrapper() {
|
export default async function CardWrapper() {
|
@ -1,5 +1,5 @@
|
|||||||
import CreateRandomInvoiceContainer from '@/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice';
|
import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/create-random-invoice/create-random-invoice';
|
||||||
import latestInvoicesController from '@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller';
|
import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
|
||||||
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { isLeft } from 'fp-ts/lib/Either';
|
import { isLeft } from 'fp-ts/lib/Either';
|
@ -1,4 +1,4 @@
|
|||||||
import revenueChartController from '@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller';
|
import revenueChartController from '@/app/dashboard/components/server/revenue-chart/revenue-chart-controller';
|
||||||
import { CalendarIcon } from '@heroicons/react/24/outline';
|
import { CalendarIcon } from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
export default async function RevenueChart() {
|
export default async function RevenueChart() {
|
@ -1,4 +1,4 @@
|
|||||||
import NavLinks from '@/app/[lang]/dashboard/components/client/nav-links/nav-links';
|
import NavLinks from '@/app/dashboard/components/client/nav-links/nav-links';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export default function SideNav() {
|
export default function SideNav() {
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import SideNav from "@/app/[lang]/dashboard/components/server/sidenav";
|
import SideNav from "@/app/dashboard/components/server/sidenav";
|
||||||
import dashboardAppModule from "@/app/[lang]/dashboard/module/dashboard-app-module";
|
import dashboardAppModule from "@/app/dashboard/module/dashboard-app-module";
|
||||||
import { DiContext } from "@/bootstrap/di/di-context";
|
import { DiContext } from "@/bootstrap/di/di-context";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
|
|
5
src/app/dashboard/loading.tsx
Normal file
5
src/app/dashboard/loading.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import DashboardSkeleton from "@/app/dashboard/components/server/skeletons/skeletons";
|
||||||
|
|
||||||
|
export default function Loading() {
|
||||||
|
return <DashboardSkeleton />;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm";
|
import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm";
|
||||||
import di from "@/bootstrap/di/init-di"
|
import di from "@/bootstrap/di/init-di"
|
||||||
import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
|
import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
|
||||||
|
|
27
src/app/dashboard/page.tsx
Normal file
27
src/app/dashboard/page.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { LatestInvoicesSkeleton, RevenueChartSkeleton } from "@/app/dashboard/components/server/skeletons/skeletons";
|
||||||
|
import CardWrapper from "@/app/dashboard/components/server/cards/cards";
|
||||||
|
import LatestInvoices from "@/app/dashboard/components/server/latest-invoices/latest-invoices";
|
||||||
|
import RevenueChart from "@/app/dashboard/components/server/revenue-chart/revenue-chart";
|
||||||
|
import { Suspense } from "react";
|
||||||
|
|
||||||
|
export default async function Dashboard() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<h1 className={`mb-4 text-xl md:text-2xl`}>
|
||||||
|
Dashboard
|
||||||
|
</h1>
|
||||||
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
|
<CardWrapper />
|
||||||
|
</div>
|
||||||
|
<div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
|
||||||
|
<Suspense fallback={<RevenueChartSkeleton />}>
|
||||||
|
<RevenueChart />
|
||||||
|
</Suspense>
|
||||||
|
<Suspense fallback={<LatestInvoicesSkeleton />}>
|
||||||
|
<LatestInvoices />
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
@ -2,12 +2,10 @@ import ButtonVm from "@/app/components/button/button-vm";
|
|||||||
import { useServerAction } from "@/bootstrap/helpers/hooks/use-server-action";
|
import { useServerAction } from "@/bootstrap/helpers/hooks/use-server-action";
|
||||||
import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
|
import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
|
||||||
import BaseVM from "@/bootstrap/helpers/vm/base-vm";
|
import BaseVM from "@/bootstrap/helpers/vm/base-vm";
|
||||||
import langKey from "@/bootstrap/i18n/dictionaries/lang-key";
|
|
||||||
import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
|
import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
|
||||||
import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
|
import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
|
||||||
import { faker } from "@faker-js/faker";
|
import { faker } from "@faker-js/faker";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
|
export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
|
||||||
private createInvoice: typeof createInvoiceUsecase
|
private createInvoice: typeof createInvoiceUsecase
|
||||||
|
|
||||||
@ -21,11 +19,9 @@ export default class CreateRandomInvoiceButtonVM extends BaseVM<ButtonVm> {
|
|||||||
const [action, isPending] = useServerAction(() => this.onClickHandler(router.refresh))
|
const [action, isPending] = useServerAction(() => this.onClickHandler(router.refresh))
|
||||||
const throttledOnClick = useThrottle(action, 5000)
|
const throttledOnClick = useThrottle(action, 5000)
|
||||||
|
|
||||||
const {t} = useTranslation()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
title: t(isPending ? langKey.global.loading : langKey.dashboard.invoice.createButton),
|
title: isPending ? "Loading" : "Create Random Invoice",
|
||||||
isDisable: isPending ? true : false
|
isDisable: isPending ? true : false
|
||||||
},
|
},
|
||||||
onClick: throttledOnClick.bind(this)
|
onClick: throttledOnClick.bind(this)
|
@ -23,7 +23,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html>
|
<html lang="en">
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
>
|
>
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
export const isServer = typeof window === 'undefined'
|
|
117
src/bootstrap/helpers/lib/actions.ts
Normal file
117
src/bootstrap/helpers/lib/actions.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { revalidatePath } from 'next/cache';
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
import { sql } from '@/bootstrap/boundaries/db/db';
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
customerId: z.string({
|
||||||
|
invalid_type_error: 'Please select a customer.',
|
||||||
|
}),
|
||||||
|
amount: z.coerce.number().gt(0, { message: 'Please enter an amount greater than $0.' }),
|
||||||
|
status: z.enum(['pending', 'paid'], {
|
||||||
|
invalid_type_error: 'Please select an invoice status.',
|
||||||
|
}),
|
||||||
|
date: z.string(),
|
||||||
|
});
|
||||||
|
const CreateInvoice = FormSchema.omit({ id: true, date: true });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This is temporary
|
||||||
|
export type State = {
|
||||||
|
errors?: {
|
||||||
|
customerId?: string[];
|
||||||
|
amount?: string[];
|
||||||
|
status?: string[];
|
||||||
|
};
|
||||||
|
message?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createInvoice(_prevState: State, formData: FormData) {
|
||||||
|
// Validate form fields using Zod
|
||||||
|
console.log('ffor', formData)
|
||||||
|
const validatedFields = CreateInvoice.safeParse({
|
||||||
|
customerId: formData?.get('customerId') || undefined,
|
||||||
|
amount: formData?.get('amount') || undefined,
|
||||||
|
status: formData?.get('status') || undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If form validation fails, return errors early. Otherwise, continue.
|
||||||
|
if (!validatedFields.success) {
|
||||||
|
return {
|
||||||
|
errors: validatedFields.error.flatten().fieldErrors,
|
||||||
|
message: 'Missing Fields. Failed to Create Invoice.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare data for insertion into the database
|
||||||
|
const { customerId, amount, status } = validatedFields.data;
|
||||||
|
const amountInCents = amount * 100;
|
||||||
|
const date = new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
|
// Insert data into the database
|
||||||
|
try {
|
||||||
|
await sql`
|
||||||
|
INSERT INTO invoices (customer_id, amount, status, date)
|
||||||
|
VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
|
||||||
|
`;
|
||||||
|
} catch {
|
||||||
|
// If a database error occurs, return a more specific error.
|
||||||
|
return {
|
||||||
|
message: 'Database Error: Failed to Create Invoice.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revalidate the cache for the invoices page and redirect the user.
|
||||||
|
revalidatePath('/dashboard/invoices');
|
||||||
|
redirect('/dashboard/invoices');
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateInvoice = FormSchema.omit({ id: true, date: true });
|
||||||
|
export async function updateInvoice(
|
||||||
|
id: string,
|
||||||
|
_prevState: State,
|
||||||
|
formData: FormData,
|
||||||
|
) {
|
||||||
|
const validatedFields = UpdateInvoice.safeParse({
|
||||||
|
customerId: formData.get('customerId'),
|
||||||
|
amount: formData.get('amount'),
|
||||||
|
status: formData.get('status'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!validatedFields.success) {
|
||||||
|
return {
|
||||||
|
errors: validatedFields.error.flatten().fieldErrors,
|
||||||
|
message: 'Missing Fields. Failed to Update Invoice.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { customerId, amount, status } = validatedFields.data;
|
||||||
|
const amountInCents = amount * 100;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sql`
|
||||||
|
UPDATE invoices
|
||||||
|
SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status}
|
||||||
|
WHERE id = ${id}
|
||||||
|
`;
|
||||||
|
} catch {
|
||||||
|
return { message: 'Database Error: Failed to Update Invoice.' };
|
||||||
|
}
|
||||||
|
|
||||||
|
revalidatePath('/dashboard/invoices');
|
||||||
|
redirect('/dashboard/invoices');
|
||||||
|
}
|
||||||
|
export async function deleteInvoice(id: string) {
|
||||||
|
try {
|
||||||
|
await sql`DELETE FROM invoices WHERE id = ${id}`;
|
||||||
|
revalidatePath('/dashboard/invoices');
|
||||||
|
return { message: 'Deleted Invoice.' };
|
||||||
|
} catch {
|
||||||
|
return { message: 'Database Error: Failed to Delete Invoice.' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
|
|
||||||
|
|
||||||
const en: typeof langKey = {
|
|
||||||
global: {
|
|
||||||
home: "Home",
|
|
||||||
loading: "Loading",
|
|
||||||
dashboard: "Dashboard"
|
|
||||||
},
|
|
||||||
dashboard: {
|
|
||||||
invoice: {
|
|
||||||
createButton: "Create random Invoice"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default en
|
|
@ -1,14 +0,0 @@
|
|||||||
const langKey = {
|
|
||||||
global: {
|
|
||||||
home: "global.home",
|
|
||||||
dashboard: "global.dashboard",
|
|
||||||
loading: "global.loading"
|
|
||||||
},
|
|
||||||
dashboard: {
|
|
||||||
invoice: {
|
|
||||||
createButton: "dashboard.invoice.createButton"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default langKey;
|
|
@ -1,16 +0,0 @@
|
|||||||
import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
|
|
||||||
|
|
||||||
const ru: typeof langKey = {
|
|
||||||
global: {
|
|
||||||
home: "Дом",
|
|
||||||
loading: "Загрузка",
|
|
||||||
dashboard: "Панель приборов"
|
|
||||||
},
|
|
||||||
dashboard: {
|
|
||||||
invoice: {
|
|
||||||
createButton: "Создать случайный счет-фактуру"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ru
|
|
@ -1,13 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { I18nextProvider } from "react-i18next"
|
|
||||||
import { initI18next } from "@/bootstrap/i18n/i18n";
|
|
||||||
import { createInstance, Resource } from "i18next";
|
|
||||||
import { PropsWithChildren } from "react";
|
|
||||||
|
|
||||||
export default function TranslationsProvider({children, lng, resources}: PropsWithChildren & {lng: string; resources: Resource}) {
|
|
||||||
const i18n = createInstance()
|
|
||||||
|
|
||||||
initI18next({lng, i18n, resources})
|
|
||||||
|
|
||||||
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import { getOptions, languages } from '@/bootstrap/i18n/settings'
|
|
||||||
import { createInstance, i18n, Resource } from 'i18next'
|
|
||||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
|
||||||
import { initReactI18next } from 'react-i18next/initReactI18next'
|
|
||||||
|
|
||||||
export const initI18next = async (params: {lng: string, i18n?: i18n, resources?: Resource, ns?: string}) => {
|
|
||||||
const { lng, i18n, ns, resources } = params
|
|
||||||
const i18nInstance = i18n ? i18n : createInstance()
|
|
||||||
await i18nInstance
|
|
||||||
.use(initReactI18next)
|
|
||||||
.use(resourcesToBackend((language: string) => import(`./dictionaries/${language}.ts`)))
|
|
||||||
.init({
|
|
||||||
...getOptions(lng, ns),
|
|
||||||
resources,
|
|
||||||
preload: resources ? [] : languages
|
|
||||||
},)
|
|
||||||
|
|
||||||
await i18nInstance.init()
|
|
||||||
|
|
||||||
return {
|
|
||||||
i18n: i18nInstance,
|
|
||||||
resources: i18nInstance.services.resourceStore.data,
|
|
||||||
t: i18nInstance.t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getServerTranslation(lng: string, ns?: string, options: {keyPrefix?: string} = {}) {
|
|
||||||
const i18nextInstance = (await initI18next({lng, ns})).i18n
|
|
||||||
|
|
||||||
return {
|
|
||||||
t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns, options?.keyPrefix),
|
|
||||||
i18n: i18nextInstance
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
export const fallbackLng = 'en'
|
|
||||||
export const languages = [fallbackLng, 'ru']
|
|
||||||
export const defaultNS = 'translation'
|
|
||||||
export const cookieName = 'i18next'
|
|
||||||
|
|
||||||
export function getOptions (lng = fallbackLng, ns = defaultNS) {
|
|
||||||
return {
|
|
||||||
// debug: true,
|
|
||||||
supportedLngs: languages,
|
|
||||||
fallbackLng,
|
|
||||||
lng,
|
|
||||||
fallbackNS: defaultNS,
|
|
||||||
defaultNS,
|
|
||||||
ns
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import { makeFailureMessage } from "@/feature/common/failures/failure-helpers";
|
|||||||
* This is a class called BaseFailure that extends the Error class. It is
|
* This is a class called BaseFailure that extends the Error class. It is
|
||||||
* used as a base class for creating custom failure classes.
|
* used as a base class for creating custom failure classes.
|
||||||
*/
|
*/
|
||||||
export default abstract class BaseFailure<META_DATA> {
|
export default abstract class BaseFailure {
|
||||||
/* ------------------------------- Attributes ------------------------------- */
|
/* ------------------------------- Attributes ------------------------------- */
|
||||||
private readonly BASE_FAILURE_MESSAGE = "failure";
|
private readonly BASE_FAILURE_MESSAGE = "failure";
|
||||||
|
|
||||||
@ -15,12 +15,8 @@ export default abstract class BaseFailure<META_DATA> {
|
|||||||
message = this.BASE_FAILURE_MESSAGE;
|
message = this.BASE_FAILURE_MESSAGE;
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
metadata: META_DATA | undefined;
|
constructor(key: string) {
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
constructor(key: string, metadata?: META_DATA) {
|
|
||||||
this.message = makeFailureMessage(this.message, key);
|
this.message = makeFailureMessage(this.message, key);
|
||||||
this.metadata = metadata ?? undefined
|
|
||||||
}
|
}
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
|
|||||||
/**
|
/**
|
||||||
* Failure for needed arguments in a method but sent wrong one
|
* Failure for needed arguments in a method but sent wrong one
|
||||||
*/
|
*/
|
||||||
export default class ArgumentsFailure<META_DATA> extends BaseDevFailure<META_DATA> {
|
export default class ArgumentsFailure extends BaseDevFailure {
|
||||||
/* ------------------------------- Constructor ------------------------------ */
|
/* ------------------------------- Constructor ------------------------------ */
|
||||||
constructor(metadata?: META_DATA) {
|
constructor() {
|
||||||
super("arguments", metadata);
|
super("arguments");
|
||||||
}
|
}
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import BaseFailure from "@/feature/common/failures/base-failure";
|
import BaseFailure from "@/feature/common/failures/base-failure";
|
||||||
|
|
||||||
export default abstract class BaseDevFailure<META_DATA> extends BaseFailure<META_DATA> {}
|
export default abstract class BaseDevFailure extends BaseFailure {}
|
||||||
|
@ -3,8 +3,8 @@ import BaseDevFailure from "@/feature/common/failures/dev/base-dev-failure";
|
|||||||
/**
|
/**
|
||||||
* This is a failure of not having specific dependency
|
* This is a failure of not having specific dependency
|
||||||
*/
|
*/
|
||||||
export default class DependencyFailure<META_DATA> extends BaseDevFailure<META_DATA> {
|
export default class DependencyFailure extends BaseDevFailure {
|
||||||
constructor(metadata: META_DATA) {
|
constructor() {
|
||||||
super("DependencyFailure", metadata);
|
super("DependencyFailure");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ import BaseFailure from "./base-failure";
|
|||||||
/**
|
/**
|
||||||
* Failure for HTTP response when response dosn't have base structure
|
* Failure for HTTP response when response dosn't have base structure
|
||||||
*/
|
*/
|
||||||
export default class NetworkFailure<META_DATA> extends BaseFailure<META_DATA> {
|
export default class NetworkFailure extends BaseFailure {
|
||||||
/* ------------------------------- Constructor ------------------------------ */
|
/* ------------------------------- Constructor ------------------------------ */
|
||||||
constructor(metaData?: META_DATA) {
|
constructor() {
|
||||||
super("network", metaData);
|
super("network");
|
||||||
}
|
}
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ import BaseFailure from "./base-failure";
|
|||||||
/**
|
/**
|
||||||
* Failure for params failure
|
* Failure for params failure
|
||||||
*/
|
*/
|
||||||
export default class ParamsFailure<META_DATA> extends BaseFailure<META_DATA> {
|
export default class ParamsFailure extends BaseFailure {
|
||||||
/* ------------------------------- Constructor ------------------------------ */
|
/* ------------------------------- Constructor ------------------------------ */
|
||||||
constructor(metadata?: META_DATA) {
|
constructor() {
|
||||||
super("params", metadata);
|
super("params");
|
||||||
}
|
}
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ type customerInvoiceDbResponse = {
|
|||||||
|
|
||||||
export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
|
export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
|
||||||
fetchList(): ApiTask<CustomerInvoice[]> {
|
fetchList(): ApiTask<CustomerInvoice[]> {
|
||||||
|
|
||||||
return pipe(
|
return pipe(
|
||||||
tryCatch(
|
tryCatch(
|
||||||
async () => {
|
async () => {
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
import { sql } from "@/bootstrap/boundaries/db/db";
|
import { sql } from "@/bootstrap/boundaries/db/db";
|
||||||
import ApiTask from "@/feature/common/data/api-task";
|
|
||||||
import { failureOr } from "@/feature/common/failures/failure-helpers";
|
|
||||||
import NetworkFailure from "@/feature/common/failures/network-failure";
|
|
||||||
import { formatCurrency } from "@/feature/common/feature-helpers";
|
import { formatCurrency } from "@/feature/common/feature-helpers";
|
||||||
import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
|
import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
|
||||||
import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
|
import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
|
||||||
import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
|
import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
|
||||||
import { pipe } from "fp-ts/lib/function";
|
|
||||||
import { tryCatch } from "fp-ts/lib/TaskEither";
|
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
|
|
||||||
type InvoiceSummaryDbResponse = {paid: string, pending: string}
|
type InvoiceSummaryDbResponse = {paid: string, pending: string}
|
||||||
@ -18,10 +13,7 @@ export default class InvoiceDbRepo implements InvoiceRepo {
|
|||||||
return data.count ?? 0
|
return data.count ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
createInvoice(params: InvoiceParam): ApiTask<string> {
|
async createInvoice(params: InvoiceParam): Promise<string> {
|
||||||
return pipe(
|
|
||||||
tryCatch(
|
|
||||||
async () => {
|
|
||||||
const firstCustomerIdDb = await sql`SELECT
|
const firstCustomerIdDb = await sql`SELECT
|
||||||
id FROM customers
|
id FROM customers
|
||||||
ORDER BY id DESC
|
ORDER BY id DESC
|
||||||
@ -41,10 +33,6 @@ export default class InvoiceDbRepo implements InvoiceRepo {
|
|||||||
RETURNING id
|
RETURNING id
|
||||||
`;
|
`;
|
||||||
return result.at(0)?.id ?? ""
|
return result.at(0)?.id ?? ""
|
||||||
},
|
|
||||||
(l) => failureOr(l, new NetworkFailure(l as Error))
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
|
async fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
|
||||||
import acceptLanguage from 'accept-language'
|
|
||||||
import { cookieName, fallbackLng, languages } from '@/bootstrap/i18n/settings'
|
|
||||||
|
|
||||||
acceptLanguage.languages(languages)
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
matcher: ["/((?!api|static|.*\\..*|_next).*)"]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function middleware(req: NextRequest) {
|
|
||||||
let lng
|
|
||||||
if (req.cookies.has(cookieName)) lng = acceptLanguage.get(req?.cookies?.get(cookieName)?.value)
|
|
||||||
if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'))
|
|
||||||
if (!lng) lng = fallbackLng
|
|
||||||
|
|
||||||
// Redirect if lng in path is not supported
|
|
||||||
if (
|
|
||||||
!languages.some(loc => req.nextUrl.pathname.startsWith(`/${loc}`)) &&
|
|
||||||
!req.nextUrl.pathname.startsWith('/_next')
|
|
||||||
) {
|
|
||||||
return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}`, req.url))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.headers.has('referer')) {
|
|
||||||
const refererUrl = new URL(req?.headers?.get('referer') ?? "")
|
|
||||||
const lngInReferer = languages.find((l) => refererUrl.pathname.startsWith(`/${l}`))
|
|
||||||
const response = NextResponse.next()
|
|
||||||
if (lngInReferer) {
|
|
||||||
response.cookies.set(cookieName, lngInReferer)
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.next()
|
|
||||||
}
|
|
@ -25,6 +25,6 @@
|
|||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/boundaries/db/seed.js", "src/bootstrap/boundaries/db/placeholder-data.js", "src/middleware.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/boundaries/db/seed.js", "src/bootstrap/boundaries/db/placeholder-data.js"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
121
yarn.lock
121
yarn.lock
@ -138,7 +138,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.25.9"
|
"@babel/helper-plugin-utils" "^7.25.9"
|
||||||
|
|
||||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0":
|
"@babel/runtime@^7.12.5":
|
||||||
version "7.26.0"
|
version "7.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
|
||||||
integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
|
integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
|
||||||
@ -334,13 +334,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.1.0.tgz#5d7957df87e2fb0eee5dcfd311ba83b34ec8eead"
|
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.1.0.tgz#5d7957df87e2fb0eee5dcfd311ba83b34ec8eead"
|
||||||
integrity sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==
|
integrity sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==
|
||||||
|
|
||||||
"@formatjs/intl-localematcher@^0.5.2":
|
|
||||||
version "0.5.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.6.tgz#cd0cd99483673d3196a15b4e2c924cfda7f002f8"
|
|
||||||
integrity sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA==
|
|
||||||
dependencies:
|
|
||||||
tslib "2"
|
|
||||||
|
|
||||||
"@heroicons/react@^2.1.5":
|
"@heroicons/react@^2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.5.tgz#1e13f34976cc542deae92353c01c8b3d7942e9ba"
|
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.5.tgz#1e13f34976cc542deae92353c01c8b3d7942e9ba"
|
||||||
@ -808,24 +801,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.20.7"
|
"@babel/types" "^7.20.7"
|
||||||
|
|
||||||
"@types/cookie@^0.6.0":
|
|
||||||
version "0.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
|
|
||||||
integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==
|
|
||||||
|
|
||||||
"@types/estree@1.0.6", "@types/estree@^1.0.0":
|
"@types/estree@1.0.6", "@types/estree@^1.0.0":
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||||
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@^3.3.5":
|
|
||||||
version "3.3.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494"
|
|
||||||
integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==
|
|
||||||
dependencies:
|
|
||||||
"@types/react" "*"
|
|
||||||
hoist-non-react-statics "^3.3.0"
|
|
||||||
|
|
||||||
"@types/json5@^0.0.29":
|
"@types/json5@^0.0.29":
|
||||||
version "0.0.29"
|
version "0.0.29"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
@ -1019,13 +999,6 @@ abbrev@1:
|
|||||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||||
|
|
||||||
accept-language@^3.0.20:
|
|
||||||
version "3.0.20"
|
|
||||||
resolved "https://registry.yarnpkg.com/accept-language/-/accept-language-3.0.20.tgz#e825601d3b59f5ac7487698569b6640e80240cda"
|
|
||||||
integrity sha512-xklPzRma4aoDEPk0ZfMjeuxB2FP4JBYlAR25OFUqCoOYDjYo6wGwAs49SnTN/MoB5VpnNX9tENfZ+vEIFmHQMQ==
|
|
||||||
dependencies:
|
|
||||||
bcp47 "^1.1.2"
|
|
||||||
|
|
||||||
acorn-jsx@^5.3.2:
|
acorn-jsx@^5.3.2:
|
||||||
version "5.3.2"
|
version "5.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||||
@ -1261,11 +1234,6 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
bcp47@^1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/bcp47/-/bcp47-1.1.2.tgz#354be3307ffd08433a78f5e1e2095845f89fc7fe"
|
|
||||||
integrity sha512-JnkkL4GUpOvvanH9AZPX38CxhiLsXMBicBY2IAtqiVN8YulGDQybUydWA4W6yAMtw6iShtw+8HEF6cfrTHU+UQ==
|
|
||||||
|
|
||||||
bcrypt@^5.1.1:
|
bcrypt@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2"
|
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2"
|
||||||
@ -1475,11 +1443,6 @@ convert-source-map@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||||
|
|
||||||
cookie@^0.7.2:
|
|
||||||
version "0.7.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
|
|
||||||
integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
|
|
||||||
|
|
||||||
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
|
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||||
@ -2372,13 +2335,6 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.2"
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
|
||||||
version "3.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
|
||||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
|
||||||
dependencies:
|
|
||||||
react-is "^16.7.0"
|
|
||||||
|
|
||||||
html-encoding-sniffer@^4.0.0:
|
html-encoding-sniffer@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448"
|
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448"
|
||||||
@ -2386,13 +2342,6 @@ html-encoding-sniffer@^4.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
whatwg-encoding "^3.1.1"
|
whatwg-encoding "^3.1.1"
|
||||||
|
|
||||||
html-parse-stringify@^3.0.1:
|
|
||||||
version "3.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
|
|
||||||
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
|
|
||||||
dependencies:
|
|
||||||
void-elements "3.1.0"
|
|
||||||
|
|
||||||
http-proxy-agent@^7.0.2:
|
http-proxy-agent@^7.0.2:
|
||||||
version "7.0.2"
|
version "7.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
|
||||||
@ -2417,27 +2366,6 @@ https-proxy-agent@^7.0.5:
|
|||||||
agent-base "^7.0.2"
|
agent-base "^7.0.2"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
i18next-browser-languagedetector@^8.0.0:
|
|
||||||
version "8.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz#b6fdd9b43af67c47f2c26c9ba27710a1eaf31e2f"
|
|
||||||
integrity sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.23.2"
|
|
||||||
|
|
||||||
i18next-resources-to-backend@^1.2.1:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d"
|
|
||||||
integrity sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.23.2"
|
|
||||||
|
|
||||||
i18next@^23.16.4:
|
|
||||||
version "23.16.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.4.tgz#79c07544f6d6fa803fe8427108d547542c1d6cf4"
|
|
||||||
integrity sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.23.2"
|
|
||||||
|
|
||||||
iconv-lite@0.6.3:
|
iconv-lite@0.6.3:
|
||||||
version "0.6.3"
|
version "0.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||||
@ -3007,19 +2935,6 @@ natural-compare@^1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
negotiator@^0.6.3:
|
|
||||||
version "0.6.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7"
|
|
||||||
integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
|
|
||||||
|
|
||||||
next-i18n-router@^5.5.1:
|
|
||||||
version "5.5.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/next-i18n-router/-/next-i18n-router-5.5.1.tgz#3d4c34d431e38aa4ba8f52cd54d4387fc70db6d2"
|
|
||||||
integrity sha512-uJGYUAQS33LbRT3Jx+kurR/E79iPQo1jWZUYmc+614UkPt58k2XYyGloSvHR74b21i4K/d6eksdBj6T2WojjdA==
|
|
||||||
dependencies:
|
|
||||||
"@formatjs/intl-localematcher" "^0.5.2"
|
|
||||||
negotiator "^0.6.3"
|
|
||||||
|
|
||||||
next@15.0.2:
|
next@15.0.2:
|
||||||
version "15.0.2"
|
version "15.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-15.0.2.tgz#4a2224c007856118010b8cef5e9b2383cd743388"
|
resolved "https://registry.yarnpkg.com/next/-/next-15.0.2.tgz#4a2224c007856118010b8cef5e9b2383cd743388"
|
||||||
@ -3369,15 +3284,6 @@ queue-microtask@^1.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||||
|
|
||||||
react-cookie@^7.2.2:
|
|
||||||
version "7.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84"
|
|
||||||
integrity sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg==
|
|
||||||
dependencies:
|
|
||||||
"@types/hoist-non-react-statics" "^3.3.5"
|
|
||||||
hoist-non-react-statics "^3.3.2"
|
|
||||||
universal-cookie "^7.0.0"
|
|
||||||
|
|
||||||
react-dom@19.0.0-rc-69d4b800-20241021:
|
react-dom@19.0.0-rc-69d4b800-20241021:
|
||||||
version "19.0.0-rc-69d4b800-20241021"
|
version "19.0.0-rc-69d4b800-20241021"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-69d4b800-20241021.tgz#e27b4f2c962236e9ece496a0ea1c9c7161608ea0"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0-rc-69d4b800-20241021.tgz#e27b4f2c962236e9ece496a0ea1c9c7161608ea0"
|
||||||
@ -3385,15 +3291,7 @@ react-dom@19.0.0-rc-69d4b800-20241021:
|
|||||||
dependencies:
|
dependencies:
|
||||||
scheduler "0.25.0-rc-69d4b800-20241021"
|
scheduler "0.25.0-rc-69d4b800-20241021"
|
||||||
|
|
||||||
react-i18next@^15.1.0:
|
react-is@^16.13.1:
|
||||||
version "15.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.1.0.tgz#9494e4add2389f04c205dd7628c1aa75747b98a3"
|
|
||||||
integrity sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.25.0"
|
|
||||||
html-parse-stringify "^3.0.1"
|
|
||||||
|
|
||||||
react-is@^16.13.1, react-is@^16.7.0:
|
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
@ -4041,7 +3939,7 @@ tsconfig-paths@^3.15.0:
|
|||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
strip-bom "^3.0.0"
|
strip-bom "^3.0.0"
|
||||||
|
|
||||||
tslib@*, tslib@2, tslib@^2.4.0:
|
tslib@*, tslib@^2.4.0:
|
||||||
version "2.8.1"
|
version "2.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||||
@ -4134,14 +4032,6 @@ undici-types@~6.19.2:
|
|||||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
|
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
|
||||||
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
|
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
|
||||||
|
|
||||||
universal-cookie@^7.0.0:
|
|
||||||
version "7.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c"
|
|
||||||
integrity sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/cookie" "^0.6.0"
|
|
||||||
cookie "^0.7.2"
|
|
||||||
|
|
||||||
update-browserslist-db@^1.1.1:
|
update-browserslist-db@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
|
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
|
||||||
@ -4209,11 +4099,6 @@ vitest@^2.1.4:
|
|||||||
vite-node "2.1.4"
|
vite-node "2.1.4"
|
||||||
why-is-node-running "^2.3.0"
|
why-is-node-running "^2.3.0"
|
||||||
|
|
||||||
void-elements@3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
|
||||||
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
|
|
||||||
|
|
||||||
w3c-xmlserializer@^5.0.0:
|
w3c-xmlserializer@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
|
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user