navbar changed
This commit is contained in:
75
package-lock.json
generated
75
package-lock.json
generated
@@ -33,6 +33,7 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"framer-motion": "^12.38.0",
|
||||||
"lucide-react": "^0.503.0",
|
"lucide-react": "^0.503.0",
|
||||||
"next": "15.5.9",
|
"next": "15.5.9",
|
||||||
"next-intl": "^4.3.9",
|
"next-intl": "^4.3.9",
|
||||||
@@ -43,7 +44,8 @@
|
|||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.2.0",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"zod": "^4.1.11"
|
"zod": "^4.1.11",
|
||||||
|
"zustand": "^5.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
@@ -5735,6 +5737,33 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.38.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
|
||||||
|
"integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.38.0",
|
||||||
|
"motion-utils": "^12.36.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -7160,6 +7189,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.38.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
|
||||||
|
"integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.36.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.36.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
|
||||||
|
"integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -9354,6 +9398,35 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zustand": {
|
||||||
|
"version": "5.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
|
||||||
|
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=18.0.0",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=18.0.0",
|
||||||
|
"use-sync-external-store": ">=1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"framer-motion": "^12.38.0",
|
||||||
"lucide-react": "^0.503.0",
|
"lucide-react": "^0.503.0",
|
||||||
"next": "15.5.9",
|
"next": "15.5.9",
|
||||||
"next-intl": "^4.3.9",
|
"next-intl": "^4.3.9",
|
||||||
@@ -46,7 +47,8 @@
|
|||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.2.0",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"zod": "^4.1.11"
|
"zod": "^4.1.11",
|
||||||
|
"zustand": "^5.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
@@ -71,4 +73,4 @@
|
|||||||
"eslint src --fix"
|
"eslint src --fix"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/features/auth/login/lib/formatPhone.ts
Normal file
13
src/features/auth/login/lib/formatPhone.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export const formatPhone = (value: string) => {
|
||||||
|
if (value.length <= 2) return value;
|
||||||
|
if (value.length <= 5) return `${value.slice(0, 2)} ${value.slice(2)}`;
|
||||||
|
if (value.length <= 7)
|
||||||
|
return `${value.slice(0, 2)} ${value.slice(2, 5)} ${value.slice(5)}`;
|
||||||
|
return `${value.slice(0, 2)} ${value.slice(2, 5)} ${value.slice(
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
)} ${value.slice(7)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const normalizeDigits = (value: string) =>
|
||||||
|
value.replace(/\D/g, '').slice(0, 9);
|
||||||
14
src/features/auth/login/lib/togle.ts
Normal file
14
src/features/auth/login/lib/togle.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface LoginModalStore {
|
||||||
|
openLoginModal: boolean;
|
||||||
|
toggleLoginModal: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useLoginModal = create<LoginModalStore>((set) => ({
|
||||||
|
openLoginModal: false,
|
||||||
|
toggleLoginModal: () =>
|
||||||
|
set((state) => ({ openLoginModal: !state.openLoginModal })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export { useLoginModal };
|
||||||
31
src/features/auth/login/lib/useLoginForm.ts
Normal file
31
src/features/auth/login/lib/useLoginForm.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
|
export function useLoginForm() {
|
||||||
|
const [phone, setPhone] = useState('');
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
|
const submit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setError('');
|
||||||
|
|
||||||
|
if (phone.length !== 9) {
|
||||||
|
setError(t('auth.phone_invalid'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionStorage.setItem('prev_page', 'login');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
phone,
|
||||||
|
setPhone,
|
||||||
|
submit,
|
||||||
|
error,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
1
src/features/auth/login/model/index.ts
Normal file
1
src/features/auth/login/model/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { RegisterModal } from './model';
|
||||||
5
src/features/auth/login/model/model.tsx
Normal file
5
src/features/auth/login/model/model.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
function RegisterModal() {
|
||||||
|
return <div></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RegisterModal };
|
||||||
119
src/features/auth/login/ui/form.tsx
Normal file
119
src/features/auth/login/ui/form.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { formatPhone, normalizeDigits } from '../lib/formatPhone';
|
||||||
|
import { Input } from '@/shared/ui/input';
|
||||||
|
import { Button } from '@/shared/ui/button';
|
||||||
|
import PhonePrefix from './phonePrefix';
|
||||||
|
import { useLoginForm } from '../lib/useLoginForm';
|
||||||
|
import { Label } from '@/shared/ui/label';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
import { useLoginModal } from '../lib/togle';
|
||||||
|
|
||||||
|
// ============================= //
|
||||||
|
|
||||||
|
export default function LoginForm() {
|
||||||
|
const t = useTranslations();
|
||||||
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
|
// ========== Handlers ========== //
|
||||||
|
|
||||||
|
const handlePhoneChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setPhone(normalizeDigits(e.target.value));
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================== //
|
||||||
|
|
||||||
|
const { phone, setPhone, submit, error, loading } = useLoginForm();
|
||||||
|
const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<header className="w-full " onClick={toggleLoginModal}>
|
||||||
|
<X />
|
||||||
|
</header>
|
||||||
|
<form onSubmit={submit} className="space-y-6 text-center">
|
||||||
|
{/* PHONE FIELD */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="phone" className="text-sm font-medium">
|
||||||
|
{t('auth.login_phone')}
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`relative transition-all duration-300 ${
|
||||||
|
isFocused ? 'scale-[1.02]' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<PhonePrefix isFocused={isFocused} />
|
||||||
|
|
||||||
|
<Input
|
||||||
|
id="phone"
|
||||||
|
type="tel"
|
||||||
|
placeholder="90 123 45 67"
|
||||||
|
value={formatPhone(phone)}
|
||||||
|
onChange={handlePhoneChange}
|
||||||
|
onFocus={() => setIsFocused(true)}
|
||||||
|
onBlur={() => setIsFocused(false)}
|
||||||
|
className={`pl-30 h-12 text-base font-medium border-2 transition-all ${
|
||||||
|
isFocused
|
||||||
|
? 'border-kok shadow-md shadow-kok/20 bg-kok/5'
|
||||||
|
: 'border-border hover:border-kok/50'
|
||||||
|
} ${error && 'border-destructive'}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Helper text */}
|
||||||
|
<div className="flex justify-between items-center px-1">
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{phone.length > 0 &&
|
||||||
|
t('auth.entered_digits', { count: phone.length })}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{phone.length === 9 && (
|
||||||
|
<span className="text-xs text-green-600 font-medium slide-in-from-right-2">
|
||||||
|
✓ {t('auth.full')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="text-sm text-destructive bg-destructive/10 p-3 rounded-md border border-destructive/20 animate-in">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading || phone.length !== 9}
|
||||||
|
className="w-full h-12 mt-5 bg-linear-to-r from-blue-600 to-indigo-600 hover:scale-[1.02] text-base font-semibold transition-all shadow-lg hover:shadow-xl active:scale-95"
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<span className="h-4 w-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||||
|
{t('auth.otp_sending')}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
t('auth.otp')
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* DIVIDER */}
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute inset-0 flex items-center">
|
||||||
|
<span className="w-full border-t border-border" />
|
||||||
|
</div>
|
||||||
|
<div className="relative flex justify-center text-xs uppercase">
|
||||||
|
<span className="bg-background px-2 text-muted-foreground">
|
||||||
|
{t('auth.or_continue')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
// Ushbu fayl loyiha structurasi uchun qo'shilgan. O'chirib tashlasangiz bo'ladi
|
// Ushbu fayl loyiha structurasi uchun qo'shilgan. O'chirib tashlasangiz bo'ladi
|
||||||
|
export { MotionWrapper } from './motion';
|
||||||
17
src/features/auth/login/ui/motion.tsx
Normal file
17
src/features/auth/login/ui/motion.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
function MotionWrapper({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: -50 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export { MotionWrapper };
|
||||||
19
src/features/auth/login/ui/phonePrefix.tsx
Normal file
19
src/features/auth/login/ui/phonePrefix.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Phone } from 'lucide-react';
|
||||||
|
|
||||||
|
function PhonePrefix({ isFocused }: { isFocused: boolean }) {
|
||||||
|
return (
|
||||||
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 flex items-center gap-2 pointer-events-none">
|
||||||
|
<Phone
|
||||||
|
className={`h-4 w-4 ${isFocused ? 'text-primary' : 'text-muted-foreground'}`}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={`font-semibold text-base ${isFocused ? 'text-primary' : 'text-muted-foreground'}`}
|
||||||
|
>
|
||||||
|
+998
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground/40">|</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PhonePrefix;
|
||||||
@@ -1 +0,0 @@
|
|||||||
// Ushbu fayl loyiha structurasi uchun qo'shilgan. O'chirib tashlasangiz bo'ladi
|
|
||||||
@@ -3,7 +3,7 @@ import { LanguageRoutes } from './types';
|
|||||||
|
|
||||||
export const routing = defineRouting({
|
export const routing = defineRouting({
|
||||||
// A list of all locales that are supported
|
// A list of all locales that are supported
|
||||||
locales: [LanguageRoutes.UZ, LanguageRoutes.RU, LanguageRoutes.KI],
|
locales: [LanguageRoutes.UZ, LanguageRoutes.RU, LanguageRoutes.EN],
|
||||||
|
|
||||||
// Used when no locale matches
|
// Used when no locale matches
|
||||||
defaultLocale: LanguageRoutes.UZ,
|
defaultLocale: LanguageRoutes.UZ,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export enum LanguageRoutes {
|
export enum LanguageRoutes {
|
||||||
UZ = 'uz', // o'zbekcha
|
UZ = 'uz', // o'zbekcha
|
||||||
RU = 'ru', // ruscha
|
RU = 'ru', // ruscha
|
||||||
KI = 'ki', // kirilcha
|
EN = 'en', // english
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/shared/ui/label.tsx
Normal file
23
src/shared/ui/label.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||||
|
import { cn } from '../lib/utils';
|
||||||
|
|
||||||
|
function Label({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
data-slot="label"
|
||||||
|
className={cn(
|
||||||
|
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Label };
|
||||||
21
src/shared/zustand/userLogin.ts
Normal file
21
src/shared/zustand/userLogin.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
surname: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserLoginStore {
|
||||||
|
user: User | null;
|
||||||
|
setUser: (user: User) => void;
|
||||||
|
clearUser: () => void;
|
||||||
|
getUser: () => User | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserLogin = create<UserLoginStore>((set, get) => ({
|
||||||
|
user: null,
|
||||||
|
setUser: (user: User) => set({ user }),
|
||||||
|
clearUser: () => set({ user: null }),
|
||||||
|
getUser: () => get().user,
|
||||||
|
}));
|
||||||
@@ -1,77 +1,23 @@
|
|||||||
import { Book, Sunset, Trees, Zap } from 'lucide-react';
|
|
||||||
import { MenuItem } from './model';
|
import { MenuItem } from './model';
|
||||||
import { LanguageRoutes } from '@/shared/config/i18n/types';
|
import { LanguageRoutes } from '@/shared/config/i18n/types';
|
||||||
|
|
||||||
const menu: MenuItem[] = [
|
const menu: MenuItem[] = [
|
||||||
{ title: 'Home', url: '#' },
|
{ title: 'About Site', url: '/about' },
|
||||||
|
// {
|
||||||
|
// title: 'Products',
|
||||||
|
// url: '#',
|
||||||
|
// items: [
|
||||||
|
// {
|
||||||
|
// title: 'Blog',
|
||||||
|
// description: 'The latest industry news, updates, and info',
|
||||||
|
// icon: Book,
|
||||||
|
// url: '#',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
title: 'Products',
|
title: 'Contact',
|
||||||
url: '#',
|
url: '/contact',
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: 'Blog',
|
|
||||||
description: 'The latest industry news, updates, and info',
|
|
||||||
icon: Book,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Company',
|
|
||||||
description: 'Our mission is to innovate and empower the world',
|
|
||||||
icon: Trees,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Careers',
|
|
||||||
description: 'Browse job listing and discover our workspace',
|
|
||||||
icon: Sunset,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Support',
|
|
||||||
description:
|
|
||||||
'Get in touch with our support team or visit our community forums',
|
|
||||||
icon: Zap,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Resources',
|
|
||||||
url: '#',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: 'Help Center',
|
|
||||||
description: 'Get all the answers you need right here',
|
|
||||||
icon: Zap,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Contact Us',
|
|
||||||
description: 'We are here to help you with any questions you have',
|
|
||||||
icon: Sunset,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Status',
|
|
||||||
description: 'Check the current status of our services and APIs',
|
|
||||||
icon: Trees,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Terms of Service',
|
|
||||||
description: 'Our terms and conditions for using our services',
|
|
||||||
icon: Book,
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Pricing',
|
|
||||||
url: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Blog',
|
|
||||||
url: '#',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -81,8 +27,8 @@ const languages: { name: string; key: LanguageRoutes }[] = [
|
|||||||
key: LanguageRoutes.UZ,
|
key: LanguageRoutes.UZ,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Ўзбекча',
|
name: 'English',
|
||||||
key: LanguageRoutes.KI,
|
key: LanguageRoutes.EN,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Русский',
|
name: 'Русский',
|
||||||
|
|||||||
59
src/widgets/navbar/ui/authButtons.tsx
Normal file
59
src/widgets/navbar/ui/authButtons.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
'use client';
|
||||||
|
import { useLoginModal } from '@/features/auth/login/lib/togle';
|
||||||
|
import { Link } from '@/shared/config/i18n/navigation';
|
||||||
|
import { Button } from '@/shared/ui/button';
|
||||||
|
import { useUserLogin } from '@/shared/zustand/userLogin';
|
||||||
|
import {
|
||||||
|
NavigationMenuContent,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuLink,
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
} from '@/shared/ui/navigation-menu';
|
||||||
|
import SubMenuLink from './SubMenuLink';
|
||||||
|
import { ChangeLang } from './ChangeLang';
|
||||||
|
|
||||||
|
function AuthButtons() {
|
||||||
|
const auth = {
|
||||||
|
login: { title: 'Login', url: '#' },
|
||||||
|
signup: { title: 'Sign up', url: '#' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const userItem = [
|
||||||
|
{ title: 'Profile', url: '/profile' },
|
||||||
|
{ title: 'Logout', url: '#' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal);
|
||||||
|
const user = useUserLogin((state) => state.user);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return (
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<NavigationMenuTrigger>{user.name}</NavigationMenuTrigger>
|
||||||
|
<NavigationMenuContent className="bg-popover text-popover-foreground">
|
||||||
|
{userItem.map((subItem) => (
|
||||||
|
<NavigationMenuLink asChild key={subItem.title} className="w-80">
|
||||||
|
<SubMenuLink item={subItem} />
|
||||||
|
</NavigationMenuLink>
|
||||||
|
))}
|
||||||
|
</NavigationMenuContent>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex lg:flex-row flex-col gap-3">
|
||||||
|
<div className="lg:flex hidden">
|
||||||
|
<ChangeLang />
|
||||||
|
</div>
|
||||||
|
<Button asChild variant="outline" onClick={toggleLoginModal}>
|
||||||
|
<Link href={auth.login.url}>{auth.login.title}</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild>
|
||||||
|
<Link href={auth.signup.url}>{auth.signup.title}</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AuthButtons };
|
||||||
@@ -18,13 +18,9 @@ import RenderMenuItem from './RenderItem';
|
|||||||
import RenderMobileMenuItem from './RenderMobileMenuItem';
|
import RenderMobileMenuItem from './RenderMobileMenuItem';
|
||||||
import { ChangeLang } from './ChangeLang';
|
import { ChangeLang } from './ChangeLang';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { AuthButtons } from './authButtons';
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const auth = {
|
|
||||||
login: { title: 'Login', url: '#' },
|
|
||||||
signup: { title: 'Sign up', url: '#' },
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-4">
|
<section className="py-4">
|
||||||
<div className="custom-container">
|
<div className="custom-container">
|
||||||
@@ -50,15 +46,7 @@ const Navbar = () => {
|
|||||||
</NavigationMenu>
|
</NavigationMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<AuthButtons />
|
||||||
<ChangeLang />
|
|
||||||
<Button asChild variant="outline">
|
|
||||||
<Link href={auth.login.url}>{auth.login.title}</Link>
|
|
||||||
</Button>
|
|
||||||
<Button asChild>
|
|
||||||
<Link href={auth.signup.url}>{auth.signup.title}</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Mobile Menu */}
|
{/* Mobile Menu */}
|
||||||
@@ -101,15 +89,7 @@ const Navbar = () => {
|
|||||||
>
|
>
|
||||||
{menu.map((item) => RenderMobileMenuItem(item))}
|
{menu.map((item) => RenderMobileMenuItem(item))}
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
<AuthButtons />
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<Button asChild variant="outline">
|
|
||||||
<Link href={auth.login.url}>{auth.login.title}</Link>
|
|
||||||
</Button>
|
|
||||||
<Button asChild>
|
|
||||||
<Link href={auth.signup.url}>{auth.signup.title}</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
|
|||||||
Reference in New Issue
Block a user