develop #3
@ -17,11 +17,14 @@
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"fp-ts": "^2.16.9",
|
||||
"i18next": "^23.16.4",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
"next": "15.0.2",
|
||||
"postgres": "^3.4.5",
|
||||
"react": "19.0.0-rc-69d4b800-20241021",
|
||||
"react-dom": "19.0.0-rc-69d4b800-20241021",
|
||||
"react-i18next": "^15.1.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import Button from "@/app/components/button/button"
|
||||
import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm"
|
||||
import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm"
|
||||
import { useDI } from "@/bootstrap/di/di-context"
|
||||
import { useRef } from "react"
|
||||
|
@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import navLinkPersonalVM from '@/app/dashboard/components/client/nav-links/nav-link-controller';
|
||||
import navLinkPersonalVM from '@/app/[lang]/dashboard/components/client/nav-links/nav-link-controller';
|
||||
import clsx from 'clsx';
|
||||
import Link from 'next/link'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import cardController from "@/app/dashboard/components/server/card/card-controller";
|
||||
import cardController from "@/app/[lang]/dashboard/components/server/card/card-controller";
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Card } from '@/app/dashboard/components/server/card/card';
|
||||
import cardsController from '@/app/dashboard/components/server/cards/cards-controller';
|
||||
import { Card } from '@/app/[lang]/dashboard/components/server/card/card';
|
||||
import cardsController from '@/app/[lang]/dashboard/components/server/cards/cards-controller';
|
||||
|
||||
|
||||
export default async function CardWrapper() {
|
@ -1,5 +1,5 @@
|
||||
import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/create-random-invoice/create-random-invoice';
|
||||
import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller';
|
||||
import CreateRandomInvoiceContainer from '@/app/[lang]/dashboard/components/client/create-random-invoice/create-random-invoice';
|
||||
import latestInvoicesController from '@/app/[lang]/dashboard/components/server/latest-invoices/latest-invoices-controller';
|
||||
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||
import clsx from 'clsx';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
@ -1,4 +1,4 @@
|
||||
import revenueChartController from '@/app/dashboard/components/server/revenue-chart/revenue-chart-controller';
|
||||
import revenueChartController from '@/app/[lang]/dashboard/components/server/revenue-chart/revenue-chart-controller';
|
||||
import { CalendarIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
export default async function RevenueChart() {
|
@ -1,4 +1,4 @@
|
||||
import NavLinks from '@/app/dashboard/components/client/nav-links/nav-links';
|
||||
import NavLinks from '@/app/[lang]/dashboard/components/client/nav-links/nav-links';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function SideNav() {
|
@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
import SideNav from "@/app/dashboard/components/server/sidenav";
|
||||
import dashboardAppModule from "@/app/dashboard/module/dashboard-app-module";
|
||||
import SideNav from "@/app/[lang]/dashboard/components/server/sidenav";
|
||||
import dashboardAppModule from "@/app/[lang]/dashboard/module/dashboard-app-module";
|
||||
import { DiContext } from "@/bootstrap/di/di-context";
|
||||
import { useRef } from "react";
|
||||
|
5
src/app/[lang]/dashboard/loading.tsx
Normal file
5
src/app/[lang]/dashboard/loading.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import DashboardSkeleton from "@/app/[lang]/dashboard/components/server/skeletons/skeletons";
|
||||
|
||||
export default function Loading() {
|
||||
return <DashboardSkeleton />;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm";
|
||||
import CreateRandomInvoiceButtonVM from "@/app/[lang]/dashboard/vm/create-random-invoice-button-vm";
|
||||
import di from "@/bootstrap/di/init-di"
|
||||
import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
|
||||
|
@ -1,15 +1,17 @@
|
||||
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 { 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 { getTranslation } from "@/bootstrap/i18n/i18n";
|
||||
|
||||
export default async function Dashboard() {
|
||||
|
||||
export default async function Dashboard(props: {params: Promise<{lang: string}>}) {
|
||||
const {lang} = await props.params
|
||||
const { t } = await getTranslation(lang)
|
||||
return (
|
||||
<main>
|
||||
<h1 className={`mb-4 text-xl md:text-2xl`}>
|
||||
Dashboard
|
||||
{t("global.dashboard")}
|
||||
</h1>
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<CardWrapper />
|
@ -1,5 +0,0 @@
|
||||
import DashboardSkeleton from "@/app/dashboard/components/server/skeletons/skeletons";
|
||||
|
||||
export default function Loading() {
|
||||
return <DashboardSkeleton />;
|
||||
}
|
15
src/bootstrap/i18n/dictionaries/en.ts
Normal file
15
src/bootstrap/i18n/dictionaries/en.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
|
||||
|
||||
const en: typeof langKey = {
|
||||
global: {
|
||||
home: "Home",
|
||||
dashboard: "Dashboard"
|
||||
},
|
||||
dashboard: {
|
||||
invoice: {
|
||||
createButton: "Create random Invoice"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default en
|
13
src/bootstrap/i18n/dictionaries/lang-key.ts
Normal file
13
src/bootstrap/i18n/dictionaries/lang-key.ts
Normal file
@ -0,0 +1,13 @@
|
||||
const langKey = {
|
||||
global: {
|
||||
home: "Дом",
|
||||
dashboard: "Панель приборов"
|
||||
},
|
||||
dashboard: {
|
||||
invoice: {
|
||||
createButton: "Создать случайный счет-фактуру"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default langKey;
|
15
src/bootstrap/i18n/dictionaries/ru.ts
Normal file
15
src/bootstrap/i18n/dictionaries/ru.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import langKey from "@/bootstrap/i18n/dictionaries/lang-key"
|
||||
|
||||
const ru: typeof langKey = {
|
||||
global: {
|
||||
home: "Дом",
|
||||
dashboard: "Панель приборов"
|
||||
},
|
||||
dashboard: {
|
||||
invoice: {
|
||||
createButton: "Создать случайный счет-фактуру"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ru
|
21
src/bootstrap/i18n/i18n.ts
Normal file
21
src/bootstrap/i18n/i18n.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { getOptions } from '@/bootstrap/i18n/settings'
|
||||
import { createInstance } from 'i18next'
|
||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||
import { initReactI18next } from 'react-i18next/initReactI18next'
|
||||
|
||||
const initI18next = async (lng: string, ns?: string) => {
|
||||
const i18nInstance = createInstance()
|
||||
await i18nInstance
|
||||
.use(initReactI18next)
|
||||
.use(resourcesToBackend((language: string) => import(`./dictionaries/${language}.ts`)))
|
||||
.init(getOptions(lng, ns))
|
||||
return i18nInstance
|
||||
}
|
||||
|
||||
export async function getTranslation(lng: string, ns?: string, options: {keyPrefix?: string} = {}) {
|
||||
const i18nextInstance = await initI18next(lng, ns)
|
||||
return {
|
||||
t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns, options?.keyPrefix),
|
||||
i18n: i18nextInstance
|
||||
}
|
||||
}
|
15
src/bootstrap/i18n/settings.ts
Normal file
15
src/bootstrap/i18n/settings.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export const fallbackLng = 'en'
|
||||
export const languages = [fallbackLng, 'ru']
|
||||
export const defaultNS = 'translation'
|
||||
|
||||
export function getOptions (lng = fallbackLng, ns = defaultNS) {
|
||||
return {
|
||||
// debug: true,
|
||||
supportedLngs: languages,
|
||||
fallbackLng,
|
||||
lng,
|
||||
fallbackNS: defaultNS,
|
||||
defaultNS,
|
||||
ns
|
||||
}
|
||||
}
|
23
src/middleware.ts
Normal file
23
src/middleware.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { fallbackLng, languages } from "@/bootstrap/i18n/settings";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl
|
||||
const pathnameHasLocale = languages.some(
|
||||
(lang) => pathname.startsWith(`/${lang}/`) || pathname === `/${lang}`
|
||||
)
|
||||
|
||||
if (pathnameHasLocale) return
|
||||
|
||||
request.nextUrl.pathname = `/${fallbackLng}${pathname}`
|
||||
// e.g. incoming request is /products
|
||||
// The new URL is now /en-US/products
|
||||
return NextResponse.redirect(request.nextUrl)
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
// Skip all internal paths (_next)
|
||||
'/((?!api|_next/static|_next/image|favicon.ico).*)'
|
||||
],
|
||||
}
|
@ -25,6 +25,6 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/bootstrap/boundaries/db/seed.js", "src/bootstrap/boundaries/db/placeholder-data.js"],
|
||||
"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"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
36
yarn.lock
36
yarn.lock
@ -138,7 +138,7 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.25.9"
|
||||
|
||||
"@babel/runtime@^7.12.5":
|
||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0":
|
||||
version "7.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
|
||||
integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
|
||||
@ -2342,6 +2342,13 @@ html-encoding-sniffer@^4.0.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
|
||||
@ -2366,6 +2373,20 @@ https-proxy-agent@^7.0.5:
|
||||
agent-base "^7.0.2"
|
||||
debug "4"
|
||||
|
||||
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:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||
@ -3291,6 +3312,14 @@ react-dom@19.0.0-rc-69d4b800-20241021:
|
||||
dependencies:
|
||||
scheduler "0.25.0-rc-69d4b800-20241021"
|
||||
|
||||
react-i18next@^15.1.0:
|
||||
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:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
@ -4099,6 +4128,11 @@ vitest@^2.1.4:
|
||||
vite-node "2.1.4"
|
||||
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:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
|
||||
|
Loading…
x
Reference in New Issue
Block a user