toast, en, show number etc
This commit is contained in:
parent
d653c15c90
commit
53920d41ef
@ -6,13 +6,15 @@ import { MapPin, Calendar, Phone, MessageCircle, Share2, Flag, Heart } from 'luc
|
||||
import { prisma } from '@/prisma/prisma-client';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ShowNumberModal } from '@/components/shared/modals/show-number';
|
||||
import { getUserSession } from '@/lib/get-user-session';
|
||||
|
||||
type Params = Promise<{ id: string }>
|
||||
|
||||
export default async function AdtPage(props: { params: Params }) {
|
||||
// const [ openShowNumberModal, setOpenShowNumberModal ] = React.useState(false)
|
||||
const params = await props.params;
|
||||
|
||||
|
||||
const session = await getUserSession();
|
||||
const adt = await prisma.adt.findFirst({
|
||||
where: {
|
||||
id: Number(params.id),
|
||||
@ -33,7 +35,6 @@ export default async function AdtPage(props: { params: Params }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <ShowNumberModal open={openShowNumberModal} onClose={() => setOpenShowNumberModal(false)} /> */}
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
@ -91,13 +92,14 @@ export default async function AdtPage(props: { params: Params }) {
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
<ShowNumberModal phoneNumber={String(adt.user?.email)} session={session} />
|
||||
{/* <button
|
||||
// onClick={() => setOpenShowNumberModal(true)}
|
||||
className="w-full flex items-center justify-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700"
|
||||
>
|
||||
<Phone className="h-5 w-5" />
|
||||
<span>Show Phone Number</span>
|
||||
</button>
|
||||
</button> */}
|
||||
<button className="w-full flex items-center justify-center gap-2 bg-white border border-indigo-600 text-indigo-600 px-4 py-2 rounded-lg hover:bg-indigo-50">
|
||||
<MessageCircle className="h-5 w-5" />
|
||||
<span>Send Message (In development)</span>
|
||||
|
||||
@ -2,47 +2,23 @@
|
||||
|
||||
import Categories from "@/components/Categories";
|
||||
import ListingCard from "@/components/ListingCard";
|
||||
import { BlockAdts } from "@/components/shared/block-adts";
|
||||
import { getUserSession } from "@/lib/get-user-session";
|
||||
import { prisma } from "@/prisma/prisma-client";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default async function Home() {
|
||||
const adts = await prisma.adt.findMany()
|
||||
const session = await getUserSession()
|
||||
|
||||
// if (!session) {
|
||||
// return redirect('/not-auth')
|
||||
// }
|
||||
|
||||
// const user = await prisma.user.findFirst({
|
||||
// where: {
|
||||
// id: Number(session?.id)
|
||||
// }
|
||||
// })
|
||||
|
||||
// console.log(user)
|
||||
console.log("session",session)
|
||||
// const session = await getUserSession()
|
||||
// // console.log(user)
|
||||
// console.log("session",session)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="min-h-screen bg-gray-100">
|
||||
<Categories />
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-xl font-semibold">Featured Listings</h2>
|
||||
<div className="flex gap-2">
|
||||
<select className="px-4 py-2 rounded-lg border border-gray-200 bg-white">
|
||||
<option>Most Recent</option>
|
||||
<option>Price: Low to High</option>
|
||||
<option>Price: High to Low</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{adts.map((adt) => (
|
||||
<ListingCard key={adt.id} title={adt.title} image={String(adt.image)} price={String(adt.price)} location={String(adt.location)} date={String(adt.createdAt)} id={String(adt.id)} />
|
||||
))}
|
||||
</div>
|
||||
<BlockAdts />
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -4,6 +4,7 @@ import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "./globals.css";
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
|
||||
|
||||
export default function RootLayout({
|
||||
@ -16,6 +17,7 @@ export default function RootLayout({
|
||||
<body>
|
||||
<SessionProvider>
|
||||
{children}
|
||||
<Toaster />
|
||||
</SessionProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -6,6 +6,7 @@ import { useSession, signIn } from 'next-auth/react';
|
||||
import { Button } from './ui/button';
|
||||
import { ProfileButton } from './shared/profile-button';
|
||||
import { AuthModal } from './shared/modals/auth-modal/auth-modal';
|
||||
import AddButton from './shared/add-button';
|
||||
|
||||
export default function Header() {
|
||||
const [openAuthModal, setOpenAuthModal] = React.useState(false)
|
||||
@ -42,24 +43,15 @@ export default function Header() {
|
||||
<Search className="h-6 w-6 text-gray-600" />
|
||||
</button>
|
||||
|
||||
<Link
|
||||
href="/adt/create"
|
||||
className="p-2 hover:bg-gray-100 rounded-full"
|
||||
>
|
||||
<PlusCircle className="h-6 w-6 text-indigo-600" />
|
||||
</Link>
|
||||
<AddButton />
|
||||
|
||||
<ProfileButton onClickSignIn={() => setOpenAuthModal(true)} />
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden sm:flex items-center gap-4">
|
||||
<Link
|
||||
href="/adt/create"
|
||||
className="p-2 hover:bg-gray-100 rounded-full"
|
||||
>
|
||||
<PlusCircle className="h-6 w-6 text-indigo-600" />
|
||||
</Link>
|
||||
<AddButton />
|
||||
|
||||
{/* <button className="relative p-2 hover:bg-gray-100 rounded-full">
|
||||
<Bell className="h-6 w-6 text-gray-600" />
|
||||
<span className="absolute top-0 right-0 h-4 w-4 bg-red-500 rounded-full text-xs text-white flex items-center justify-center">
|
||||
|
||||
@ -24,7 +24,7 @@ export default function ListingCard({ id, title, price, location, image, date }:
|
||||
alt={title}
|
||||
className="w-full h-full object-cover rounded-t-xl"
|
||||
/>
|
||||
<button
|
||||
{/* <button
|
||||
className="absolute top-3 right-3 p-2 bg-white/90 rounded-full hover:bg-white"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -32,7 +32,7 @@ export default function ListingCard({ id, title, price, location, image, date }:
|
||||
}}
|
||||
>
|
||||
<Heart className="h-5 w-5 text-gray-600" />
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
|
||||
38
components/shared/add-button.tsx
Normal file
38
components/shared/add-button.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { PlusCircle } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { AuthModal } from './modals/auth-modal/auth-modal';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export default function AddButton() {
|
||||
const [openAuthModal, setOpenAuthModal] = useState(false);
|
||||
const { data: session } = useSession();
|
||||
|
||||
const handleClick = () => {
|
||||
if (!session) {
|
||||
toast.error("Authorization required")
|
||||
setOpenAuthModal(true);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
href={session ? "/adt/create" : "#"}
|
||||
onClick={handleClick}
|
||||
className="p-2 hover:bg-gray-100 rounded-full"
|
||||
>
|
||||
<PlusCircle className="h-6 w-6 text-indigo-600" />
|
||||
</Link>
|
||||
|
||||
<AuthModal
|
||||
open={openAuthModal}
|
||||
onClose={() => setOpenAuthModal(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -7,6 +7,7 @@ import { formAdtCreateSchema, type TFormAdtCreateValues } from './schemas';
|
||||
import Image from 'next/image';
|
||||
import { Category } from '@prisma/client';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
interface CreateAdtFormProps {
|
||||
categories: Category[];
|
||||
@ -59,7 +60,9 @@ export default function AdtCreateForm({ categories }: CreateAdtFormProps) {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Ошибка при создании объявления');
|
||||
const err = await response.text()
|
||||
toast.error("Error creating adt: " + err)
|
||||
throw new Error('Error creating adt');
|
||||
}
|
||||
|
||||
reset();
|
||||
@ -67,6 +70,8 @@ export default function AdtCreateForm({ categories }: CreateAdtFormProps) {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
|
||||
toast.success("Adt created successfully")
|
||||
// Здесь можно добавить уведомление об успешном создании
|
||||
const data_f = await response.json();
|
||||
// Редирект на страницу созданного объявления
|
||||
@ -76,6 +81,7 @@ export default function AdtCreateForm({ categories }: CreateAdtFormProps) {
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
// toast.error("Error creating adt: " + error)
|
||||
// Здесь можно добавить обработку ошибок
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
|
||||
@ -2,10 +2,10 @@ import {z} from 'zod'
|
||||
|
||||
export const formAdtCreateSchema = z.object({
|
||||
title: z.string()
|
||||
.min(5, 'Заголовок должен содержать минимум 5 символов')
|
||||
.min(2, 'Заголовок должен содержать минимум 5 символов')
|
||||
.max(100, 'Заголовок не может быть длиннее 100 символов'),
|
||||
description: z.string()
|
||||
.min(20, 'Описание должно содержать минимум 20 символов')
|
||||
.min(10, 'Описание должно содержать минимум 20 символов')
|
||||
.max(1000, 'Описание не может быть длиннее 1000 символов')
|
||||
.nullable(),
|
||||
price: z.string()
|
||||
|
||||
158
components/shared/block-adts.tsx
Normal file
158
components/shared/block-adts.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
"use client"
|
||||
|
||||
// Импортируем необходимые компоненты и типы
|
||||
import { FC, useEffect, useState, useRef, useCallback } from 'react'
|
||||
import ListingCard from '../ListingCard'
|
||||
import { Adt } from '@prisma/client'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
interface BlockAdtsProps {}
|
||||
|
||||
export const BlockAdts: FC<BlockAdtsProps> = () => {
|
||||
|
||||
// Состояния для хранения объявлений и управления их отображением
|
||||
const [adts, setAdts] = useState<Adt[]>([]) // Массив объявлений
|
||||
const [sortBy, setSortBy] = useState('new') // Тип сортировки
|
||||
const [isLoading, setIsLoading] = useState(true) // Флаг загрузки
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false) // Флаг загрузки дополнительных объявлений
|
||||
const [page, setPage] = useState(1) // Текущая страница
|
||||
const [hasMore, setHasMore] = useState(true) // Флаг наличия дополнительных объявлений
|
||||
|
||||
// Создаем наблюдатель для бесконечной прокрутки
|
||||
const observer = useRef<IntersectionObserver>()
|
||||
const lastAdtElementRef = useCallback((node: HTMLDivElement) => {
|
||||
if (isLoadingMore) return
|
||||
if (observer.current) observer.current.disconnect()
|
||||
observer.current = new IntersectionObserver(entries => {
|
||||
// Если последний элемент виден и есть еще объявления - загружаем следующую порцию
|
||||
if (entries[0].isIntersecting && hasMore) {
|
||||
loadMore()
|
||||
}
|
||||
})
|
||||
if (node) observer.current.observe(node)
|
||||
}, [isLoadingMore, hasMore])
|
||||
|
||||
// Функция загрузки объявлений
|
||||
const loadAdts = async (pageNum: number, isLoadMore = false) => {
|
||||
try {
|
||||
// Устанавливаем соответствующий флаг загрузки
|
||||
if (isLoadMore) {
|
||||
setIsLoadingMore(true)
|
||||
} else {
|
||||
setIsLoading(true)
|
||||
}
|
||||
|
||||
// Запрашиваем данные с сервера
|
||||
const response = await fetch(`/api/adt?page=${pageNum}&sort=${sortBy}`)
|
||||
const { data: newAdts, meta } = await response.json()
|
||||
|
||||
// Обновляем список объявлений
|
||||
if (pageNum === 1) {
|
||||
setAdts(newAdts)
|
||||
} else {
|
||||
setAdts(prev => [...prev, ...newAdts])
|
||||
}
|
||||
|
||||
// Проверяем, есть ли еще объявления для загрузки
|
||||
setHasMore(newAdts.length > 0 && pageNum < meta.totalPages)
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки объявлений:', error)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
setIsLoadingMore(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Загружаем объявления при изменении способа сортировки
|
||||
useEffect(() => {
|
||||
loadAdts(1)
|
||||
}, [sortBy])
|
||||
|
||||
// Обработчик изменения сортировки
|
||||
const handleSort = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setSortBy(event.target.value)
|
||||
setAdts([])
|
||||
setPage(1)
|
||||
setHasMore(true)
|
||||
}
|
||||
|
||||
// Функция загрузки дополнительных объявлений
|
||||
const loadMore = () => {
|
||||
if (!isLoadingMore && hasMore) {
|
||||
const nextPage = page + 1
|
||||
setPage(nextPage)
|
||||
loadAdts(nextPage, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Компонент-заглушка для отображения во время загрузки
|
||||
const SkeletonCard = () => (
|
||||
<div className="bg-white rounded-lg shadow-md p-4 animate-pulse">
|
||||
<div className="w-full h-48 bg-gray-200 rounded-lg mb-4"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-1/2 mb-2"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-1/4"></div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Заголовок и селектор сортировки */}
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-xl font-semibold">Listings</h2>
|
||||
<div className="flex gap-2">
|
||||
<select
|
||||
className="px-4 py-2 rounded-lg border border-gray-200 bg-white"
|
||||
onChange={handleSort}
|
||||
value={sortBy}
|
||||
>
|
||||
<option value="new">Newest</option>
|
||||
<option value="price_asc">Price: ascending</option>
|
||||
<option value="price_desc">Price: descending</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Сетка объявлений */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{isLoading ? (
|
||||
// Отображаем заглушки во время начальной загрузки
|
||||
<>
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
</>
|
||||
) : (
|
||||
// Отображаем список объявлений
|
||||
adts.map((adt, index) => (
|
||||
<div
|
||||
key={adt.id}
|
||||
ref={index === adts.length - 1 ? lastAdtElementRef : undefined}
|
||||
>
|
||||
<ListingCard
|
||||
title={adt.title}
|
||||
image={String(adt.image)}
|
||||
price={String(adt.price)}
|
||||
location={String(adt.location)}
|
||||
date={String(adt.createdAt)}
|
||||
id={String(adt.id)}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Отображаем заглушки при загрузке дополнительных объявлений */}
|
||||
{isLoadingMore && (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mt-6">
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -19,6 +19,7 @@ export const AuthModal: React.FC<Props> = ({ open, onClose}) => {
|
||||
|
||||
const handleClose = () => {
|
||||
onClose()
|
||||
setType('login')
|
||||
}
|
||||
|
||||
return (
|
||||
@ -31,7 +32,7 @@ export const AuthModal: React.FC<Props> = ({ open, onClose}) => {
|
||||
|
||||
<hr />
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
{/* <Button
|
||||
variant='secondary'
|
||||
onClick={() =>
|
||||
signIn('github', {
|
||||
@ -44,9 +45,9 @@ export const AuthModal: React.FC<Props> = ({ open, onClose}) => {
|
||||
>
|
||||
<img className="2-6 h-6" src="https://github.githubassets.com/favicons/favicon.svg" />
|
||||
GitHub
|
||||
</Button>
|
||||
</Button> */}
|
||||
|
||||
<Button
|
||||
{/* <Button
|
||||
variant='secondary'
|
||||
onClick={() =>
|
||||
signIn('google', {
|
||||
@ -59,11 +60,11 @@ export const AuthModal: React.FC<Props> = ({ open, onClose}) => {
|
||||
>
|
||||
<img className="2-6 h-6" src="https://fonts.gstatic.com/s/i/productlogos/googleg/v6/24px.svg" />
|
||||
Google
|
||||
</Button>
|
||||
</Button> */}
|
||||
|
||||
</div>
|
||||
<Button variant='outline' onClick={onSwitchType} type='button' className="h-12">
|
||||
{type === 'login' ? 'Регистрация' : 'Войти'}
|
||||
{type === 'login' ? 'Sign Up' : 'Login'}
|
||||
</Button>
|
||||
</DialogContent>
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { Title } from "@/components/shared/title";
|
||||
import { FormInput } from "@/components/shared/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { signIn } from "next-auth/react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
interface Props {
|
||||
onClose?: VoidFunction;
|
||||
@ -28,11 +29,13 @@ export const LoginForm: React.FC<Props> = ({onClose}) => {
|
||||
})
|
||||
|
||||
if (!resp?.ok) {
|
||||
toast.error("Incorrect login or password")
|
||||
throw Error();
|
||||
}
|
||||
|
||||
onClose?.()
|
||||
} catch (error) {
|
||||
toast.error("Error login")
|
||||
console.error('Error [LOGIN]', error)
|
||||
}
|
||||
}
|
||||
@ -42,8 +45,8 @@ export const LoginForm: React.FC<Props> = ({onClose}) => {
|
||||
<form className="flex flex-col gap-5" onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<div className="fles justify-between items-center">
|
||||
<div className="mr-2">
|
||||
<Title text="Вход в аккаунт" size='md' className="font-bold" />
|
||||
<p className="text-gray-400">Введите свою почту, чтобы войти</p>
|
||||
<Title text="Login" size='md' className="font-bold" />
|
||||
<p className="text-gray-400">Enter your email to login</p>
|
||||
</div>
|
||||
{/* <img src="..." /> */}
|
||||
</div>
|
||||
@ -52,7 +55,7 @@ export const LoginForm: React.FC<Props> = ({onClose}) => {
|
||||
|
||||
<Button loading={form.formState.isSubmitting} className="h-12 text-base" type='submit' >
|
||||
{
|
||||
'Войти'
|
||||
'Login'
|
||||
}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@ -7,6 +7,7 @@ import { FormInput } from "@/components/shared/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { registerUser } from "@/app/actions";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
interface Props {
|
||||
onClose?: VoidFunction;
|
||||
@ -35,22 +36,24 @@ export const RegisterForm: React.FC<Props> = ({onClose}) => {
|
||||
// throw Error();
|
||||
// }
|
||||
|
||||
toast.success("User registered successfully. Login")
|
||||
|
||||
onClose?.()
|
||||
} catch (error) {
|
||||
console.error('Error [LOGIN]', error)
|
||||
toast.error("Error register")
|
||||
console.error('Error [REGISTER]', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form className="flex flex-col gap-5" onSubmit={form.handleSubmit(onSubmit)}>
|
||||
{/* <div className="fles justify-between items-center">
|
||||
<div className="fles justify-between items-center">
|
||||
<div className="mr-2">
|
||||
<Title text="Вход в аккаунт" size='md' className="font-bold" />
|
||||
<p className="text-gray-400">Введите свою почту, чтобы войти</p>
|
||||
<Title text="Sign Up" size='md' className="font-bold" />
|
||||
<p className="text-gray-400">Enter your name and email to sign up</p>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<FormInput name='name' label='Name' required />
|
||||
<FormInput name='email' label='E-Mail' required />
|
||||
<FormInput name='password' label='Password' required />
|
||||
|
||||
@ -1,45 +1,46 @@
|
||||
import { Dialog } from "@/components/ui/dialog";
|
||||
'use client'
|
||||
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
import { getUserSession } from "@/lib/get-user-session";
|
||||
import React from "react";
|
||||
import { Phone } from "lucide-react";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
phoneNumber: string;
|
||||
session: any;
|
||||
}
|
||||
|
||||
export const ShowNumberModal: React.FC<Props> = ({ open, onClose}) => {
|
||||
|
||||
// const [ session, setSession ] = React.useState(null)
|
||||
export const ShowNumberModal: React.FC<Props> = ({ phoneNumber, session }) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
// const [session, setSession] = React.useState<any>(null);
|
||||
|
||||
// React.useEffect(() => {
|
||||
// const getSession = async () => {
|
||||
// const userSession = await getUserSession()
|
||||
// setSession(userSession)
|
||||
// }
|
||||
// getSession()
|
||||
// }, [])
|
||||
// const session = await getUserSession()
|
||||
const handleClose = () => {
|
||||
onClose()
|
||||
}
|
||||
// const userSession = await getUserSession();
|
||||
// setSession(userSession);
|
||||
// };
|
||||
// getSession();
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleClose}>
|
||||
{/* <DialogContent className="w-[450px] bg-white p-10"> */}
|
||||
{/* {
|
||||
// session && (
|
||||
// <h1 className="text-2xl font-bold mb-4">892348924823</h1>
|
||||
// )
|
||||
} */}
|
||||
<>
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="w-full flex items-center justify-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700"
|
||||
>
|
||||
<Phone className="h-5 w-5" />
|
||||
<span>Show Phone Number</span>
|
||||
</button>
|
||||
|
||||
{/* <h1 className="text-2xl font-bold mb-4">892348924823</h1> */}
|
||||
<hr />
|
||||
<div className="flex gap-2">
|
||||
|
||||
|
||||
</div>
|
||||
{/* </DialogContent> */}
|
||||
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="w-[450px] bg-white p-10">
|
||||
{session ? (
|
||||
<h1 className="text-2xl font-bold mb-4">{phoneNumber}</h1>
|
||||
) : (
|
||||
<h2 className="text-xl text-center">Please login to see the phone number</h2>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -11,6 +11,7 @@ import { Title } from "./title";
|
||||
import { FormInput } from "./form/form-input";
|
||||
import { Button } from "../ui/button";
|
||||
import { updateUserInfo } from "@/app/actions";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
interface Props {
|
||||
data: User
|
||||
@ -35,13 +36,13 @@ export const ProfileForm: React.FC<Props> = ({ data }) => {
|
||||
password: formData.password,
|
||||
});
|
||||
|
||||
// toast.error('Данные обновлены 📝', {
|
||||
// icon: '✅',
|
||||
// });
|
||||
toast.success('Data updated 📝', {
|
||||
icon: '✅',
|
||||
});
|
||||
} catch (error) {
|
||||
// return toast.error('Ошибка при обновлении данных', {
|
||||
// icon: '❌',
|
||||
// });
|
||||
return toast.error('Error updating data', {
|
||||
icon: '❌',
|
||||
});
|
||||
console.log('error update', error)
|
||||
}
|
||||
};
|
||||
|
||||
27
package-lock.json
generated
27
package-lock.json
generated
@ -25,6 +25,7 @@
|
||||
"react": "19.0.0-rc-66855b96-20241106",
|
||||
"react-dom": "19.0.0-rc-66855b96-20241106",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.8"
|
||||
@ -2492,7 +2493,6 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
@ -3795,6 +3795,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.16",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
|
||||
"integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
@ -5659,6 +5668,22 @@
|
||||
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
|
||||
"integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"goober": "^2.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
"react": "19.0.0-rc-66855b96-20241106",
|
||||
"react-dom": "19.0.0-rc-66855b96-20241106",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.8"
|
||||
|
||||
@ -97,5 +97,152 @@ export const categories = [
|
||||
image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?auto=format&fit=crop&w=800",
|
||||
// date: "Posted 7 days ago"
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "2020 Tesla Model 3 Long Range",
|
||||
price: "$41,999",
|
||||
location: "San Francisco, CA",
|
||||
image: "https://images.unsplash.com/photo-1560958089-b8a1929cea89?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Винтажный велосипед",
|
||||
price: "$450",
|
||||
location: "Portland, OR",
|
||||
image: "https://images.unsplash.com/photo-1485965120184-e220f721d03e?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Профессиональный набор для рисования",
|
||||
price: "$299",
|
||||
location: "Seattle, WA",
|
||||
image: "https://images.unsplash.com/photo-1513364776144-60967b0f800f?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Кожаный диван",
|
||||
price: "$1,299",
|
||||
location: "Austin, TX",
|
||||
image: "https://images.unsplash.com/photo-1493663284031-b7e3aefcae8e?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Коллекционные виниловые пластинки",
|
||||
price: "$599",
|
||||
location: "Nashville, TN",
|
||||
image: "https://images.unsplash.com/photo-1539375665275-f9de415ef9ac?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Беговая дорожка NordicTrack",
|
||||
price: "$899",
|
||||
location: "Phoenix, AZ",
|
||||
image: "https://images.unsplash.com/photo-1540497077202-7c8a3999166f?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Набор кухонной посуды",
|
||||
price: "$399",
|
||||
location: "Boston, MA",
|
||||
image: "https://images.unsplash.com/photo-1556911220-bff31c812dba?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Горный велосипед Trek",
|
||||
price: "$789",
|
||||
location: "Denver, CO",
|
||||
image: "https://images.unsplash.com/photo-1576435728678-68d0fbf94e91?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Игровая приставка PS5",
|
||||
price: "$499",
|
||||
location: "Las Vegas, NV",
|
||||
image: "https://images.unsplash.com/photo-1606144042614-b2417e99c4e3?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Антикварный комод",
|
||||
price: "$850",
|
||||
location: "Charleston, SC",
|
||||
image: "https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Дрон DJI Mavic Air 2",
|
||||
price: "$799",
|
||||
location: "San Diego, CA",
|
||||
image: "https://images.unsplash.com/photo-1579829366248-204fe8413f31?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Электрогитара Fender",
|
||||
price: "$1,199",
|
||||
location: "Atlanta, GA",
|
||||
image: "https://images.unsplash.com/photo-1564186763535-ebb21ef5277f?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Кофемашина Breville",
|
||||
price: "$599",
|
||||
location: "Seattle, WA",
|
||||
image: "https://images.unsplash.com/photo-1517080317843-0b926a6ce350?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Умные часы Apple Watch",
|
||||
price: "$349",
|
||||
location: "Houston, TX",
|
||||
image: "https://images.unsplash.com/photo-1546868871-7041f2a55e12?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Винтажная печатная машинка",
|
||||
price: "$299",
|
||||
location: "Portland, ME",
|
||||
image: "https://images.unsplash.com/photo-1558522195-e1201b090344?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Телескоп Celestron",
|
||||
price: "$899",
|
||||
location: "Tucson, AZ",
|
||||
image: "https://images.unsplash.com/photo-1566004100631-35d015d6a491?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Набор для йоги",
|
||||
price: "$89",
|
||||
location: "Santa Monica, CA",
|
||||
image: "https://images.unsplash.com/photo-1544367567-0f2fcb009e0b?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Винный холодильник",
|
||||
price: "$599",
|
||||
location: "Napa Valley, CA",
|
||||
image: "https://images.unsplash.com/photo-1585703900468-13c7a978ad86?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Электросамокат Xiaomi",
|
||||
price: "$399",
|
||||
location: "Chicago, IL",
|
||||
image: "https://images.unsplash.com/photo-1589999562311-56254d5d4325?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
},
|
||||
{
|
||||
title: "Коллекционные монеты",
|
||||
price: "$2,999",
|
||||
location: "Washington, DC",
|
||||
image: "https://images.unsplash.com/photo-1566321343730-237ec35e53f3?auto=format&fit=crop&w=800",
|
||||
userId: 2
|
||||
},
|
||||
{
|
||||
title: "Профессиональный микрофон",
|
||||
price: "$299",
|
||||
location: "Nashville, TN",
|
||||
image: "https://images.unsplash.com/photo-1590602847861-f357a9332bbc?auto=format&fit=crop&w=800",
|
||||
userId: 1
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user