diff --git a/messages/en.json b/messages/en.json index 55bb284..a59a961 100644 --- a/messages/en.json +++ b/messages/en.json @@ -202,12 +202,12 @@ "title": "Your Personality Type", "error": "Something went wrong. Please try again later." }, - "Breath": { + "Meditation": { "title": "Stop and breathe to help you relax and focus on what really matters.", "subtitle": "Breathing practice will help improve your aura. Breath in the positive energy, breathe out the negative...", "button": "BEGIN" }, - "BreathResult": { + "MeditationResult": { "breath_relax": "Breath & Relax", "breath_in": "Breath in", "breath_out": "Breath out" diff --git a/src/app/[locale]/(core)/advisers/page.tsx b/src/app/[locale]/(core)/advisers/page.tsx new file mode 100644 index 0000000..1da4cfc --- /dev/null +++ b/src/app/[locale]/(core)/advisers/page.tsx @@ -0,0 +1,15 @@ +import { Suspense } from "react"; + +import { + AdvisersSection, + AdvisersSectionSkeleton, +} from "@/components/domains/dashboard"; +import { loadAssistants } from "@/entities/dashboard/loaders"; + +export default function Advisers() { + return ( + }> + + + ); +} diff --git a/src/app/[locale]/(core)/compatibility/page.tsx b/src/app/[locale]/(core)/compatibility/page.tsx new file mode 100644 index 0000000..6c35128 --- /dev/null +++ b/src/app/[locale]/(core)/compatibility/page.tsx @@ -0,0 +1,18 @@ +import { Suspense } from "react"; + +import { + CompatibilitySection, + CompatibilitySectionSkeleton, +} from "@/components/domains/dashboard"; +import { loadCompatibility } from "@/entities/dashboard/loaders"; + +export default function Compatibility() { + return ( + }> + + + ); +} diff --git a/src/app/[locale]/(core)/layout.module.scss b/src/app/[locale]/(core)/layout.module.scss index 048dbc7..3577e50 100644 --- a/src/app/[locale]/(core)/layout.module.scss +++ b/src/app/[locale]/(core)/layout.module.scss @@ -1,6 +1,6 @@ .main { padding: 16px; - padding-bottom: 64px; + padding-bottom: 120px; } .navBar { diff --git a/src/app/[locale]/(core)/layout.tsx b/src/app/[locale]/(core)/layout.tsx index 648b925..2d4389c 100644 --- a/src/app/[locale]/(core)/layout.tsx +++ b/src/app/[locale]/(core)/layout.tsx @@ -1,4 +1,5 @@ -import { DrawerProvider, NavigationBar } from "@/components/layout"; +import { DrawerProvider, Header } from "@/components/layout"; +import NavigationBar from "@/components/layout/NavigationBar/NavigationBar"; import styles from "./layout.module.scss"; @@ -9,8 +10,9 @@ export default function CoreLayout({ }>) { return ( - +
{children}
+ ); } diff --git a/src/app/[locale]/(core)/breath/[id]/page.module.scss b/src/app/[locale]/(core)/meditation/[id]/page.module.scss similarity index 100% rename from src/app/[locale]/(core)/breath/[id]/page.module.scss rename to src/app/[locale]/(core)/meditation/[id]/page.module.scss diff --git a/src/app/[locale]/(core)/breath/[id]/page.tsx b/src/app/[locale]/(core)/meditation/[id]/page.tsx similarity index 71% rename from src/app/[locale]/(core)/breath/[id]/page.tsx rename to src/app/[locale]/(core)/meditation/[id]/page.tsx index 657b03b..2cdcbf2 100644 --- a/src/app/[locale]/(core)/breath/[id]/page.tsx +++ b/src/app/[locale]/(core)/meditation/[id]/page.tsx @@ -1,9 +1,9 @@ -import { BreathPage } from "@/components/domains/breath"; +import { MeditationPage } from "@/components/domains/meditation"; import { startGeneration } from "@/entities/generations/api"; import styles from "./page.module.scss"; -export default async function Breath({ +export default async function Meditation({ params, }: { params: Promise<{ id: string }>; @@ -17,7 +17,7 @@ export default async function Breath({ }); return (
- +
); } diff --git a/src/app/[locale]/(core)/breath/[id]/result/[resultId]/page.module.scss b/src/app/[locale]/(core)/meditation/[id]/result/[resultId]/page.module.scss similarity index 100% rename from src/app/[locale]/(core)/breath/[id]/result/[resultId]/page.module.scss rename to src/app/[locale]/(core)/meditation/[id]/result/[resultId]/page.module.scss diff --git a/src/app/[locale]/(core)/breath/[id]/result/[resultId]/page.tsx b/src/app/[locale]/(core)/meditation/[id]/result/[resultId]/page.tsx similarity index 70% rename from src/app/[locale]/(core)/breath/[id]/result/[resultId]/page.tsx rename to src/app/[locale]/(core)/meditation/[id]/result/[resultId]/page.tsx index 71185ef..82d9a01 100644 --- a/src/app/[locale]/(core)/breath/[id]/result/[resultId]/page.tsx +++ b/src/app/[locale]/(core)/meditation/[id]/result/[resultId]/page.tsx @@ -1,9 +1,9 @@ -import { BreathResultPage } from "@/components/domains/breath"; +import { MeditationResultPage } from "@/components/domains/meditation"; import { loadPalms } from "@/entities/dashboard/loaders"; import styles from "./page.module.scss"; -export default async function BreathResult({ +export default async function MeditationResult({ params, }: { params: Promise<{ id: string; resultId: string }>; @@ -15,7 +15,7 @@ export default async function BreathResult({ return (
- +
); } diff --git a/src/app/[locale]/(core)/breath/layout.tsx b/src/app/[locale]/(core)/meditation/layout.tsx similarity index 83% rename from src/app/[locale]/(core)/breath/layout.tsx rename to src/app/[locale]/(core)/meditation/layout.tsx index d2cd127..04f0f5c 100644 --- a/src/app/[locale]/(core)/breath/layout.tsx +++ b/src/app/[locale]/(core)/meditation/layout.tsx @@ -1,6 +1,6 @@ import { FullScreenModalProvider } from "@/providers/fullscreen-blur-modal-provider"; -export default function BreathLayout({ +export default function MeditationLayout({ children, }: { children: React.ReactNode; diff --git a/src/app/[locale]/(core)/meditation/page.tsx b/src/app/[locale]/(core)/meditation/page.tsx new file mode 100644 index 0000000..a4bec0e --- /dev/null +++ b/src/app/[locale]/(core)/meditation/page.tsx @@ -0,0 +1,18 @@ +import { Suspense } from "react"; + +import { + MeditationSection, + MeditationSectionSkeleton, +} from "@/components/domains/dashboard"; +import { loadMeditations } from "@/entities/dashboard/loaders"; + +export default function Meditation() { + return ( + }> + + + ); +} diff --git a/src/components/domains/breath/index.ts b/src/components/domains/breath/index.ts deleted file mode 100644 index 78cd845..0000000 --- a/src/components/domains/breath/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as BreathPage } from "./BreathPage/BreathPage"; -export { default as BreathResultPage } from "./BreathResultPage/BreathResultPage"; -export { default as StartBreathModalChild } from "./StartBreathModalChild/StartBreathModalChild"; diff --git a/src/components/domains/dashboard/cards/CompatibilityCard/CompatibilityCard.tsx b/src/components/domains/dashboard/cards/CompatibilityCard/CompatibilityCard.tsx index 7930c67..9925cf2 100644 --- a/src/components/domains/dashboard/cards/CompatibilityCard/CompatibilityCard.tsx +++ b/src/components/domains/dashboard/cards/CompatibilityCard/CompatibilityCard.tsx @@ -1,7 +1,6 @@ import Image from "next/image"; -import { Card, MetaLabel, Typography } from "@/components/ui"; -import { IconName } from "@/components/ui/Icon/Icon"; +import { Card, IconName, MetaLabel, Typography } from "@/components/ui"; import { Action } from "@/entities/dashboard/types"; import styles from "./CompatibilityCard.module.scss"; diff --git a/src/components/domains/dashboard/cards/MeditationCard/MeditationCard.tsx b/src/components/domains/dashboard/cards/MeditationCard/MeditationCard.tsx index 203589d..e70ac8b 100644 --- a/src/components/domains/dashboard/cards/MeditationCard/MeditationCard.tsx +++ b/src/components/domains/dashboard/cards/MeditationCard/MeditationCard.tsx @@ -1,27 +1,41 @@ import Image from "next/image"; +import clsx from "clsx"; -import { Button, Card, Icon, MetaLabel, Typography } from "@/components/ui"; -import { IconName } from "@/components/ui/Icon/Icon"; +import { + Button, + Card, + Icon, + IconName, + MetaLabel, + Typography, +} from "@/components/ui"; import { Action } from "@/entities/dashboard/types"; import styles from "./MeditationCard.module.scss"; -type MeditationCardProps = Action; +interface MeditationCardProps extends Action { + className?: string; +} export default function MeditationCard({ imageUrl, title, type, minutes, + className, }: MeditationCardProps) { return ( - + Meditation image
diff --git a/src/components/domains/dashboard/cards/PalmCard/PalmCard.tsx b/src/components/domains/dashboard/cards/PalmCard/PalmCard.tsx index f216ccf..5cb649b 100644 --- a/src/components/domains/dashboard/cards/PalmCard/PalmCard.tsx +++ b/src/components/domains/dashboard/cards/PalmCard/PalmCard.tsx @@ -1,7 +1,6 @@ import Image from "next/image"; -import { Card, MetaLabel, Typography } from "@/components/ui"; -import { IconName } from "@/components/ui/Icon/Icon"; +import { Card, IconName, MetaLabel, Typography } from "@/components/ui"; import { Action } from "@/entities/dashboard/types"; import styles from "./PalmCard.module.scss"; diff --git a/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.module.scss b/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.module.scss index 6858c69..bcd02ce 100644 --- a/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.module.scss +++ b/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.module.scss @@ -9,6 +9,10 @@ .grid { padding-right: 16px; + + &.vertical { + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)) !important; + } } .skeleton.skeleton { diff --git a/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx b/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx index db2193d..75999e8 100644 --- a/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx +++ b/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx @@ -1,4 +1,5 @@ import { use } from "react"; +import clsx from "clsx"; import { Grid, Section, Skeleton } from "@/components/ui"; import { Assistant } from "@/entities/dashboard/types"; @@ -7,17 +8,24 @@ import { AdviserCard } from "../../cards"; import styles from "./AdvisersSection.module.scss"; +interface AdvisersSectionProps { + promise: Promise; + gridDisplayMode?: "vertical" | "horizontal"; +} + export default function AdvisersSection({ promise, -}: { - promise: Promise; -}) { + gridDisplayMode = "horizontal", +}: AdvisersSectionProps) { const assistants = use(promise); const columns = Math.ceil(assistants?.length / 2); return (
- + {assistants.map(adviser => ( ))} diff --git a/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.module.scss b/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.module.scss index 2dd837e..ec22f16 100644 --- a/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.module.scss +++ b/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.module.scss @@ -9,6 +9,10 @@ .grid { padding-right: 16px; + + &.vertical { + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)) !important; + } } .skeleton.skeleton { diff --git a/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx b/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx index cf98a10..b0455c8 100644 --- a/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx +++ b/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx @@ -2,6 +2,7 @@ import { use } from "react"; import Link from "next/link"; +import clsx from "clsx"; import { Grid, Section, Skeleton } from "@/components/ui"; import { Action } from "@/entities/dashboard/types"; @@ -11,17 +12,24 @@ import { CompatibilityCard } from "../../cards"; import styles from "./CompatibilitySection.module.scss"; +interface CompatibilitySectionProps { + promise: Promise; + gridDisplayMode?: "vertical" | "horizontal"; +} + export default function CompatibilitySection({ promise, -}: { - promise: Promise; -}) { + gridDisplayMode = "horizontal", +}: CompatibilitySectionProps) { const compatibilities = use(promise); const columns = Math.ceil(compatibilities?.length / 2); return (
- + {compatibilities.map(compatibility => ( ; + gridDisplayMode?: "vertical" | "horizontal"; +} + export default function MeditationSection({ promise, -}: { - promise: Promise; -}) { + gridDisplayMode = "horizontal", +}: MeditationSectionProps) { const meditations = use(promise); const columns = meditations?.length; return (
- + {meditations.map(meditation => ( - - + + ))} diff --git a/src/components/domains/breath/BreathPage/BreathPage.tsx b/src/components/domains/meditation/MeditationPage/MeditationPage.tsx similarity index 79% rename from src/components/domains/breath/BreathPage/BreathPage.tsx rename to src/components/domains/meditation/MeditationPage/MeditationPage.tsx index b772d86..4edc910 100644 --- a/src/components/domains/breath/BreathPage/BreathPage.tsx +++ b/src/components/domains/meditation/MeditationPage/MeditationPage.tsx @@ -8,17 +8,17 @@ import { ROUTES } from "@/shared/constants/client-routes"; import { StartBreathModalChild } from ".."; -interface BreathPageProps { +interface MeditationPageProps { id: string; resultId: string; } -export default function BreathPage({ id, resultId }: BreathPageProps) { +export default function MeditationPage({ id, resultId }: MeditationPageProps) { const router = useRouter(); const { openModal, closeModal } = useFullScreenModal(); const handleBegin = useCallback(() => { - router.push(ROUTES.breathResult(id, resultId)); + router.push(ROUTES.meditationResult(id, resultId)); closeModal(); }, [closeModal, id, resultId, router]); diff --git a/src/components/domains/breath/BreathResultPage/BreathResultPage.module.scss b/src/components/domains/meditation/MeditationResultPage/MeditationResultPage.module.scss similarity index 100% rename from src/components/domains/breath/BreathResultPage/BreathResultPage.module.scss rename to src/components/domains/meditation/MeditationResultPage/MeditationResultPage.module.scss diff --git a/src/components/domains/breath/BreathResultPage/BreathResultPage.tsx b/src/components/domains/meditation/MeditationResultPage/MeditationResultPage.tsx similarity index 92% rename from src/components/domains/breath/BreathResultPage/BreathResultPage.tsx rename to src/components/domains/meditation/MeditationResultPage/MeditationResultPage.tsx index 793a173..bbe2457 100644 --- a/src/components/domains/breath/BreathResultPage/BreathResultPage.tsx +++ b/src/components/domains/meditation/MeditationResultPage/MeditationResultPage.tsx @@ -9,18 +9,18 @@ import { Action } from "@/entities/dashboard/types"; import { useGenerationPolling } from "@/hooks/generation/useGenerationPolling"; import { useToast } from "@/providers/toast-provider"; -import styles from "./BreathResultPage.module.scss"; +import styles from "./MeditationResultPage.module.scss"; -interface BreathResultPageProps { +interface MeditationResultPageProps { id: string; action: Action | undefined; } -export default function BreathResultPage({ +export default function MeditationResultPage({ id, action, -}: BreathResultPageProps) { - const t = useTranslations("BreathResult"); +}: MeditationResultPageProps) { + const t = useTranslations("MeditationResult"); const { data, error, isLoading } = useGenerationPolling(id); const { addToast } = useToast(); diff --git a/src/components/domains/breath/StartBreathModalChild/StartBreathModalChild.module.scss b/src/components/domains/meditation/StartBreathModalChild/StartBreathModalChild.module.scss similarity index 100% rename from src/components/domains/breath/StartBreathModalChild/StartBreathModalChild.module.scss rename to src/components/domains/meditation/StartBreathModalChild/StartBreathModalChild.module.scss diff --git a/src/components/domains/breath/StartBreathModalChild/StartBreathModalChild.tsx b/src/components/domains/meditation/StartBreathModalChild/StartBreathModalChild.tsx similarity index 97% rename from src/components/domains/breath/StartBreathModalChild/StartBreathModalChild.tsx rename to src/components/domains/meditation/StartBreathModalChild/StartBreathModalChild.tsx index 00069c0..99ed29a 100644 --- a/src/components/domains/breath/StartBreathModalChild/StartBreathModalChild.tsx +++ b/src/components/domains/meditation/StartBreathModalChild/StartBreathModalChild.tsx @@ -9,7 +9,7 @@ interface IStartBreathModalChildProps { } function StartBreathModalChild({ handleBegin }: IStartBreathModalChildProps) { - const t = useTranslations("Breath"); + const t = useTranslations("Meditation"); return (
diff --git a/src/components/domains/meditation/index.ts b/src/components/domains/meditation/index.ts new file mode 100644 index 0000000..5b7e3ee --- /dev/null +++ b/src/components/domains/meditation/index.ts @@ -0,0 +1,3 @@ +export { default as MeditationPage } from "./MeditationPage/MeditationPage"; +export { default as MeditationResultPage } from "./MeditationResultPage/MeditationResultPage"; +export { default as StartBreathModalChild } from "./StartBreathModalChild/StartBreathModalChild"; diff --git a/src/components/layout/Drawer/Drawer.tsx b/src/components/layout/Drawer/Drawer.tsx index cbfbb1a..bfd1bf8 100644 --- a/src/components/layout/Drawer/Drawer.tsx +++ b/src/components/layout/Drawer/Drawer.tsx @@ -3,8 +3,7 @@ import Link from "next/link"; import clsx from "clsx"; -import { Button, Icon, Typography } from "@/components/ui"; -import { IconName } from "@/components/ui/Icon/Icon"; +import { Button, Icon, IconName, Typography } from "@/components/ui"; import { ROUTES } from "@/shared/constants/client-routes"; import styles from "./Drawer.module.scss"; diff --git a/src/components/layout/Header/Header.module.scss b/src/components/layout/Header/Header.module.scss new file mode 100644 index 0000000..0f6fd6c --- /dev/null +++ b/src/components/layout/Header/Header.module.scss @@ -0,0 +1,29 @@ +.header { + width: 100%; + min-height: 56px; + height: fit-content; + padding: 16px; + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; +} + +.header > :first-child { + justify-self: start; +} + +.header > :nth-child(2) { + justify-self: center; +} + +.header > :nth-child(n + 3) { + justify-self: end; + display: inline-flex; + gap: 16px; +} + +.menuButton.menuButton { + padding: 0; + width: fit-content; + background: none; +} diff --git a/src/components/layout/Header/Header.tsx b/src/components/layout/Header/Header.tsx new file mode 100644 index 0000000..132c9d7 --- /dev/null +++ b/src/components/layout/Header/Header.tsx @@ -0,0 +1,36 @@ +"use client"; + +import Link from "next/link"; +import clsx from "clsx"; + +import { Button, Icon, IconName } from "@/components/ui"; +import { ROUTES } from "@/shared/constants/client-routes"; + +import Logo from "../Logo/Logo"; + +import styles from "./Header.module.scss"; + +import { useDrawer } from ".."; + +interface HeaderProps { + className?: string; +} + +export default function Header({ className }: HeaderProps) { + const { open } = useDrawer(); + + return ( +
+ + + + +
+ + +
+
+ ); +} diff --git a/src/components/layout/NavigationBar/NavigationBar.module.scss b/src/components/layout/NavigationBar/NavigationBar.module.scss index 0f6fd6c..d372f40 100644 --- a/src/components/layout/NavigationBar/NavigationBar.module.scss +++ b/src/components/layout/NavigationBar/NavigationBar.module.scss @@ -1,29 +1,43 @@ -.header { - width: 100%; - min-height: 56px; - height: fit-content; - padding: 16px; - display: grid; - grid-template-columns: 1fr auto 1fr; +.container { + width: calc(100% - 32px); + max-width: 400px; + position: fixed; + bottom: calc(0dvh + 14px); + left: 50%; + z-index: 9995; + transform: translateX(-50%); + display: flex; align-items: center; + justify-content: space-around; + // gap: 40px; + background-color: #e5e7eb; + border-radius: 24px; + padding: 16px 24px 12px; } -.header > :first-child { - justify-self: start; -} +.item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 5px; + position: relative; -.header > :nth-child(2) { - justify-self: center; -} + & .badge { + position: absolute; + bottom: 7px; + left: 17px; + } -.header > :nth-child(n + 3) { - justify-self: end; - display: inline-flex; - gap: 16px; -} + & > .label { + font-size: 11px; + line-height: 14px; + color: #8a8d93; + } -.menuButton.menuButton { - padding: 0; - width: fit-content; - background: none; + &.active { + & > .label { + color: #007aff; + } + } } diff --git a/src/components/layout/NavigationBar/NavigationBar.tsx b/src/components/layout/NavigationBar/NavigationBar.tsx index 6cf28a5..1c7b986 100644 --- a/src/components/layout/NavigationBar/NavigationBar.tsx +++ b/src/components/layout/NavigationBar/NavigationBar.tsx @@ -1,37 +1,51 @@ "use client"; import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { useLocale } from "next-intl"; import clsx from "clsx"; -import { Button, Icon } from "@/components/ui"; -import { IconName } from "@/components/ui/Icon/Icon"; +import { Badge, Icon, Typography } from "@/components/ui"; import { ROUTES } from "@/shared/constants/client-routes"; - -import Logo from "../Logo/Logo"; +import { navItems } from "@/shared/constants/navigation"; +import { stripLocale } from "@/shared/utils/path"; import styles from "./NavigationBar.module.scss"; -import { useDrawer } from ".."; - -interface NavigationBarProps { - className?: string; -} - -export default function NavigationBar({ className }: NavigationBarProps) { - const { open } = useDrawer(); +export default function NavigationBar() { + const pathname = usePathname(); + const locale = useLocale(); + const pathnameWithoutLocale = stripLocale(pathname, locale); return ( -
- - - - -
- - -
-
+ ); } diff --git a/src/components/layout/index.ts b/src/components/layout/index.ts index 24f5dfa..c711061 100644 --- a/src/components/layout/index.ts +++ b/src/components/layout/index.ts @@ -1,4 +1,4 @@ export { DrawerProvider, useDrawer } from "./Drawer/DrawerContext"; +export { default as Header } from "./Header/Header"; export { default as Logo } from "./Logo/Logo"; -export { default as NavigationBar } from "./NavigationBar/NavigationBar"; export { default as StepperBar } from "./StepperBar/StepperBar"; diff --git a/src/components/ui/Badge/Badge.module.scss b/src/components/ui/Badge/Badge.module.scss new file mode 100644 index 0000000..74c164c --- /dev/null +++ b/src/components/ui/Badge/Badge.module.scss @@ -0,0 +1,11 @@ +.badge { + display: flex; + align-items: center; + justify-content: center; + padding: 4px; + min-width: fit-content; + min-height: fit-content; + aspect-ratio: 1/1; + border-radius: 50%; + background-color: #ff0028; +} diff --git a/src/components/ui/Badge/Badge.tsx b/src/components/ui/Badge/Badge.tsx new file mode 100644 index 0000000..0b37787 --- /dev/null +++ b/src/components/ui/Badge/Badge.tsx @@ -0,0 +1,12 @@ +import clsx from "clsx"; + +import styles from "./Badge.module.scss"; + +interface BadgeProps { + children: React.ReactNode; + className?: string; +} + +export default function Badge({ children, className }: BadgeProps) { + return
{children}
; +} diff --git a/src/components/ui/Icon/Icon.tsx b/src/components/ui/Icon/Icon.tsx index 8e11fcb..cc853de 100644 --- a/src/components/ui/Icon/Icon.tsx +++ b/src/components/ui/Icon/Icon.tsx @@ -1,14 +1,21 @@ import { CSSProperties, ReactNode } from "react"; import clsx from "clsx"; -import ArticleIcon from "./icons/Article"; -import ChevronIcon from "./icons/Chevron"; -import CrossIcon from "./icons/Cross"; -import MenuIcon from "./icons/Menu"; -import NotificationIcon from "./icons/Notification"; -import SearchIcon from "./icons/Search"; -import StarIcon from "./icons/Star"; -import VideoIcon from "./icons/Video"; +import { + ArticleIcon, + ChatIcon, + ChevronIcon, + ClipboardIcon, + CrossIcon, + HeartIcon, + HomeIcon, + LeafIcon, + MenuIcon, + NotificationIcon, + SearchIcon, + StarIcon, + VideoIcon, +} from "./icons"; export enum IconName { Notification, @@ -19,6 +26,11 @@ export enum IconName { Chevron, Star, Cross, + Home, + Chat, + Clipboard, + Heart, + Leaf, } const icons: Record< @@ -33,6 +45,11 @@ const icons: Record< [IconName.Chevron]: ChevronIcon, [IconName.Star]: StarIcon, [IconName.Cross]: CrossIcon, + [IconName.Home]: HomeIcon, + [IconName.Chat]: ChatIcon, + [IconName.Clipboard]: ClipboardIcon, + [IconName.Heart]: HeartIcon, + [IconName.Leaf]: LeafIcon, }; export type IconProps = { diff --git a/src/components/ui/Icon/icons/Chat.tsx b/src/components/ui/Icon/icons/Chat.tsx new file mode 100644 index 0000000..9c86f14 --- /dev/null +++ b/src/components/ui/Icon/icons/Chat.tsx @@ -0,0 +1,37 @@ +import { SVGProps } from "react"; + +export default function ChatIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/Icon/icons/Clipboard.tsx b/src/components/ui/Icon/icons/Clipboard.tsx new file mode 100644 index 0000000..2737005 --- /dev/null +++ b/src/components/ui/Icon/icons/Clipboard.tsx @@ -0,0 +1,47 @@ +import { SVGProps } from "react"; + +export default function ClipboardIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/Icon/icons/Heart.tsx b/src/components/ui/Icon/icons/Heart.tsx new file mode 100644 index 0000000..0183c8f --- /dev/null +++ b/src/components/ui/Icon/icons/Heart.tsx @@ -0,0 +1,36 @@ +import { SVGProps } from "react"; + +export default function HeartIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/Icon/icons/Home.tsx b/src/components/ui/Icon/icons/Home.tsx new file mode 100644 index 0000000..14ecb2a --- /dev/null +++ b/src/components/ui/Icon/icons/Home.tsx @@ -0,0 +1,37 @@ +import { SVGProps } from "react"; + +export default function HomeIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/Icon/icons/Leaf.tsx b/src/components/ui/Icon/icons/Leaf.tsx new file mode 100644 index 0000000..6021116 --- /dev/null +++ b/src/components/ui/Icon/icons/Leaf.tsx @@ -0,0 +1,36 @@ +import { SVGProps } from "react"; + +export default function Leaf(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/Icon/icons/index.ts b/src/components/ui/Icon/icons/index.ts new file mode 100644 index 0000000..f8dc85c --- /dev/null +++ b/src/components/ui/Icon/icons/index.ts @@ -0,0 +1,13 @@ +export { default as ArticleIcon } from "./Article"; +export { default as ChatIcon } from "./Chat"; +export { default as ChevronIcon } from "./Chevron"; +export { default as ClipboardIcon } from "./Clipboard"; +export { default as CrossIcon } from "./Cross"; +export { default as HeartIcon } from "./Heart"; +export { default as HomeIcon } from "./Home"; +export { default as LeafIcon } from "./Leaf"; +export { default as MenuIcon } from "./Menu"; +export { default as NotificationIcon } from "./Notification"; +export { default as SearchIcon } from "./Search"; +export { default as StarIcon } from "./Star"; +export { default as VideoIcon } from "./Video"; diff --git a/src/components/ui/IconLabel/IconLabel.tsx b/src/components/ui/IconLabel/IconLabel.tsx index 6c0c568..558b6f6 100644 --- a/src/components/ui/IconLabel/IconLabel.tsx +++ b/src/components/ui/IconLabel/IconLabel.tsx @@ -1,10 +1,10 @@ import { ReactNode } from "react"; import clsx from "clsx"; -import Icon, { IconProps } from "../Icon/Icon"; - import styles from "./IconLabel.module.scss"; +import { Icon, IconProps } from ".."; + export type IconLabelProps = { iconProps: IconProps; children: ReactNode; diff --git a/src/components/ui/MetaLabel/MetaLabel.tsx b/src/components/ui/MetaLabel/MetaLabel.tsx index 1331b18..f844d1e 100644 --- a/src/components/ui/MetaLabel/MetaLabel.tsx +++ b/src/components/ui/MetaLabel/MetaLabel.tsx @@ -1,11 +1,12 @@ import { ReactNode } from "react"; -import IconLabel, { IconLabelProps } from "../IconLabel/IconLabel"; import Typography from "../Typography/Typography"; import styles from "./MetaLabel.module.scss"; -export type MetaLabelProps = { +import { IconLabel, IconLabelProps } from ".."; + +type MetaLabelProps = { iconLabelProps: IconLabelProps; children: ReactNode; }; diff --git a/src/components/ui/Modal/Modal.tsx b/src/components/ui/Modal/Modal.tsx index d9af9b9..994c1d3 100644 --- a/src/components/ui/Modal/Modal.tsx +++ b/src/components/ui/Modal/Modal.tsx @@ -3,11 +3,9 @@ import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; import clsx from "clsx"; -import { IconName } from "../Icon/Icon"; - import styles from "./Modal.module.scss"; -import { Button, Icon } from ".."; +import { Button, Icon, IconName } from ".."; interface ModalProps { children: ReactNode; diff --git a/src/components/ui/Stars/Stars.tsx b/src/components/ui/Stars/Stars.tsx index 92301d4..96af383 100644 --- a/src/components/ui/Stars/Stars.tsx +++ b/src/components/ui/Stars/Stars.tsx @@ -1,9 +1,9 @@ import clsx from "clsx"; -import Icon, { IconName } from "../Icon/Icon"; - import styles from "./Stars.module.scss"; +import { Icon, IconName } from ".."; + interface StarsProps { rating?: number; size?: number; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index a25f831..02238ef 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -1,3 +1,4 @@ +export { default as Badge } from "./Badge/Badge"; export { default as Button } from "./Button/Button"; export { default as Card } from "./Card/Card"; export { default as CircleArrow } from "./CircleArrow/CircleArrow"; @@ -5,8 +6,11 @@ export { default as EmailInput } from "./EmailInput/EmailInput"; export { default as FullScreenBlurModal } from "./FullScreenBlurModal/FullScreenBlurModal"; export { default as GPTAnimationText } from "./GPTAnimationText/GPTAnimationText"; export { default as Grid } from "./Grid/Grid"; -export { default as Icon } from "./Icon/Icon"; -export { default as IconLabel } from "./IconLabel/IconLabel"; +export { default as Icon, IconName, type IconProps } from "./Icon/Icon"; +export { + default as IconLabel, + type IconLabelProps, +} from "./IconLabel/IconLabel"; export { default as MetaLabel } from "./MetaLabel/MetaLabel"; export { default as Modal } from "./Modal/Modal"; export { default as NameInput } from "./NameInput/NameInput"; diff --git a/src/components/widgets/DatePicker/DatePicker.tsx b/src/components/widgets/DatePicker/DatePicker.tsx index cabfaf8..6219e15 100644 --- a/src/components/widgets/DatePicker/DatePicker.tsx +++ b/src/components/widgets/DatePicker/DatePicker.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useMemo, useState } from "react"; -import { useTranslations } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; import { Typography } from "@/components/ui"; import { SelectInput } from "@/components/ui/SelectInput/SelectInput"; @@ -20,9 +20,7 @@ const isValidDate = (year: number, month: number, day: number) => { // Упрощенное определение порядка полей даты на основе локали. // В реальном приложении здесь лучше использовать данные из next-intl. -const getDateInputLocaleFormat = (): ("d" | "m" | "y")[] => { - const locale = - typeof navigator !== "undefined" ? navigator.language : "en-US"; +const getDateInputLocaleFormat = (locale: string): ("d" | "m" | "y")[] => { const format = new Intl.DateTimeFormat(locale).format(new Date(2001, 1, 3)); // Используем 3/Feb/2001 if (/^3.*2/.test(format)) return ["d", "m", "y"]; // 3/2/2001 -> d/m/y if (/^2.*3/.test(format)) return ["m", "d", "y"]; // 2/3/2001 -> m/d/y @@ -49,6 +47,8 @@ export default function DatePicker({ onBlur, }: DatePickerProps) { const t = useTranslations("DatePicker"); + const locale = useLocale(); + const [year, setYear] = useState(""); const [month, setMonth] = useState(""); const [day, setDay] = useState(""); @@ -110,7 +110,10 @@ export default function DatePicker({ })); }, [year, month]); - const localeFormat = useMemo(() => getDateInputLocaleFormat(), []); + const localeFormat = useMemo( + () => getDateInputLocaleFormat(locale), + [locale] + ); const inputs = { d: ( diff --git a/src/middleware.ts b/src/middleware.ts index 56226f0..3445948 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -39,7 +39,7 @@ const intlMiddleware = createMiddleware(routing); export default async function middleware(request: NextRequest) { const authResponse = await createAuthMiddleware()(request); - if (authResponse.status !== 200) { + if (authResponse.status !== 200 && process.env.NODE_ENV === "production") { return authResponse; } diff --git a/src/shared/constants/client-routes.ts b/src/shared/constants/client-routes.ts index 9777ba9..e1e5de2 100644 --- a/src/shared/constants/client-routes.ts +++ b/src/shared/constants/client-routes.ts @@ -3,8 +3,8 @@ const ROOT_ROUTE = "/"; const profilePrefix = "profile"; const retainingFunnelPrefix = "retaining"; -const createRoute = (segments: string[]): string => { - return ROOT_ROUTE + segments.join("/"); +const createRoute = (segments: Array): string => { + return ROOT_ROUTE + segments.filter(Boolean).join("/"); }; export const ROUTES = { @@ -13,13 +13,16 @@ export const ROUTES = { // Auth authCallback: () => createRoute(["auth", "callback"]), - // Breath - breath: (id: string) => createRoute(["breath", id]), - breathResult: (id: string, resultId: string) => - createRoute(["breath", id, "result", resultId]), + // Advisers + advisers: () => createRoute(["advisers"]), + + // Meditation + meditation: (id?: string) => createRoute(["meditation", id]), + meditationResult: (id: string, resultId: string) => + createRoute(["meditation", id, "result", resultId]), // Compatibility - compatibility: (id: string) => createRoute(["compatibility", id]), + compatibility: (id?: string) => createRoute(["compatibility", id]), compatibilityResult: (id: string, resultId: string) => createRoute(["compatibility", id, "result", resultId]), @@ -56,4 +59,13 @@ export const ROUTES = { payment: () => createRoute(["payment"]), paymentSuccess: () => createRoute(["payment", "success"]), paymentFailed: () => createRoute(["payment", "failed"]), + + // Chat + chat: () => createRoute(["chat"]), + + // // Compatibility + // compatibilities: () => createRoute(["compatibilities"]), + + // // Meditation + // meditations: () => createRoute(["meditations"]), }; diff --git a/src/shared/constants/navigation.tsx b/src/shared/constants/navigation.tsx new file mode 100644 index 0000000..59e7e49 --- /dev/null +++ b/src/shared/constants/navigation.tsx @@ -0,0 +1,45 @@ +import { IconName } from "@/components/ui"; + +import { ROUTES } from "./client-routes"; + +interface NavItem { + key: string; + label: string; + icon: IconName; + href: string; + badge?: number; +} + +export const navItems: NavItem[] = [ + { + key: "home", + label: "Home", + icon: IconName.Home, + href: ROUTES.home(), + }, + { + key: "chat", + label: "Chat", + icon: IconName.Chat, + href: ROUTES.chat(), + badge: 12, + }, + { + key: "advisers", + label: "Advi...", + icon: IconName.Clipboard, + href: ROUTES.advisers(), + }, + { + key: "compatibility", + label: "Comp...", + icon: IconName.Heart, + href: ROUTES.compatibility(), + }, + { + key: "meditation", + label: "Medi...", + icon: IconName.Leaf, + href: ROUTES.meditation(), + }, +]; diff --git a/src/shared/utils/path.ts b/src/shared/utils/path.ts new file mode 100644 index 0000000..3e16ea5 --- /dev/null +++ b/src/shared/utils/path.ts @@ -0,0 +1,6 @@ +export function stripLocale(pathname: string, locale: string) { + if (pathname.startsWith(`/${locale}`)) { + return pathname.slice(locale.length + 1) || "/"; + } + return pathname; +}