Fixed build issues
This commit is contained in:
+3
-3
@@ -1,11 +1,11 @@
|
||||
FROM node:20.16.0 AS builder
|
||||
FROM node:22.19.0 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
# RUN apk add --no-cache libc6-compat
|
||||
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
RUN npm i
|
||||
|
||||
|
||||
COPY . .
|
||||
@@ -14,7 +14,7 @@ RUN npm run build
|
||||
|
||||
#---
|
||||
|
||||
FROM node:20.16.0 AS runner
|
||||
FROM node:22.19.0 AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
import { ShadowBox } from "@/src/components";
|
||||
"use client";
|
||||
|
||||
const EditCustomer = () => {
|
||||
return <ShadowBox>Edit customer</ShadowBox>;
|
||||
import { use } from "react";
|
||||
import {
|
||||
useCustomersInfiniteQuery,
|
||||
useGetCustomersDetails,
|
||||
} from "../../../../hooks/queries/useCustomerQueries";
|
||||
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
|
||||
import CreateCustomer from "@/components/forms/customers/CreateCustomer";
|
||||
import Loading from "@/app/loading";
|
||||
|
||||
const EditCustomer = ({ params }: { params: Promise<{ id: string }> }) => {
|
||||
const { id } = use(params);
|
||||
const { isLoading, data, isError } = useGetCustomersDetails(id);
|
||||
const breadcrumbItems = [
|
||||
{ name: "Dashboard", href: "/" },
|
||||
{ name: "Customers", href: "/customers" },
|
||||
{ name: "Edit Customer", href: `/customers/edit/${id}` },
|
||||
];
|
||||
if (isLoading) return <Loading />;
|
||||
if (isError) return <div>Error</div>;
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
<CreateCustomer editMode defaultValues={data.data} id={id} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditCustomer;
|
||||
|
||||
@@ -8,7 +8,7 @@ export function ContactForm() {
|
||||
<ShowcaseSection title="Contact Form" className="!p-6.5">
|
||||
<form action="#">
|
||||
<div className="mb-4.5 flex flex-col gap-4.5 xl:flex-row">
|
||||
<InputGroup
|
||||
{/* <InputGroup
|
||||
label="First name"
|
||||
type="text"
|
||||
placeholder="Enter your first name"
|
||||
@@ -20,10 +20,10 @@ export function ContactForm() {
|
||||
type="text"
|
||||
placeholder="Enter your last name"
|
||||
className="w-full xl:w-1/2"
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
|
||||
<InputGroup
|
||||
{/* <InputGroup
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="Enter your email address"
|
||||
@@ -49,7 +49,7 @@ export function ContactForm() {
|
||||
]}
|
||||
/>
|
||||
|
||||
<TextAreaGroup label="Message" placeholder="Type your message" />
|
||||
<TextAreaGroup label="Message" placeholder="Type your message" /> */}
|
||||
|
||||
<button className="mt-6 flex w-full justify-center rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90">
|
||||
Send Message
|
||||
|
||||
@@ -7,7 +7,7 @@ export function SignInForm() {
|
||||
return (
|
||||
<ShowcaseSection title="Sign In Form" className="!p-6.5">
|
||||
<form action="#">
|
||||
<InputGroup
|
||||
{/* <InputGroup
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="Enter your email address"
|
||||
@@ -18,7 +18,7 @@ export function SignInForm() {
|
||||
label="Password"
|
||||
type="password"
|
||||
placeholder="Enter your password"
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<div className="mb-5.5 mt-5 flex items-center justify-between">
|
||||
<Checkbox label="Remember me" minimal withBg withIcon="check" />
|
||||
|
||||
@@ -5,7 +5,7 @@ export function SignUpForm() {
|
||||
return (
|
||||
<ShowcaseSection title="Sign Up Form" className="!p-6.5">
|
||||
<form action="#">
|
||||
<InputGroup
|
||||
{/* <InputGroup
|
||||
label="Name"
|
||||
type="text"
|
||||
placeholder="Enter full name"
|
||||
@@ -31,7 +31,7 @@ export function SignUpForm() {
|
||||
type="password"
|
||||
placeholder="Re-type password"
|
||||
className="mb-5.5"
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<button className="flex w-full justify-center rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90">
|
||||
Sign Up
|
||||
|
||||
@@ -62,13 +62,13 @@ export function PersonalInfoForm() {
|
||||
height="sm"
|
||||
/>
|
||||
|
||||
<TextAreaGroup
|
||||
{/* <TextAreaGroup
|
||||
className="mb-5.5"
|
||||
label="BIO"
|
||||
placeholder="Write your bio here"
|
||||
icon={<PencilSquareIcon />}
|
||||
defaultValue="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam lacinia turpis tortor, consequat efficitur mi congue a. Curabitur cursus, ipsum ut lobortis sodales, enim arcu pellentesque lectus ac suscipit diam sem a felis. Cras sapien ex, blandit eu dui et suscipit gravida nunc. Sed sed est quis dui."
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<button
|
||||
|
||||
@@ -8,15 +8,16 @@ export const metadata: Metadata = {
|
||||
|
||||
export default function SettingsPage() {
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-[1080px]">
|
||||
<div className="grid grid-cols-5 gap-8">
|
||||
<div className="col-span-5 xl:col-span-3">
|
||||
<PersonalInfoForm />
|
||||
</div>
|
||||
<div className="col-span-5 xl:col-span-2">
|
||||
<UploadPhotoForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>Settings</div>
|
||||
// <div className="mx-auto w-full max-w-[1080px]">
|
||||
// <div className="grid grid-cols-5 gap-8">
|
||||
// <div className="col-span-5 xl:col-span-3">
|
||||
// <PersonalInfoForm />
|
||||
// </div>
|
||||
// <div className="col-span-5 xl:col-span-2">
|
||||
// <UploadPhotoForm />
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,49 +2,64 @@
|
||||
import { PasswordIcon, UserIcon } from "@/assets/icons";
|
||||
|
||||
import InputGroup from "../FormElements/InputGroup";
|
||||
import { Controller, SubmitHandler, useForm } from "react-hook-form";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import { ILoginCredentials } from "@/lib/api";
|
||||
import { useLoginQuery } from "@/hooks/queries";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
import { FormErrorMessages } from "@/constants/Texts";
|
||||
|
||||
export default function SigninWithPassword() {
|
||||
const { handleSubmit, control, reset } = useForm<ILoginCredentials>();
|
||||
const [passwordFieldInputType, setPasswordFieldInputType] = useState<
|
||||
"password" | "text"
|
||||
>("password");
|
||||
const { handleSubmit, control, reset, register } =
|
||||
useForm<ILoginCredentials>();
|
||||
const { mutate, isPending } = useLoginQuery();
|
||||
const passwordInputRef = useRef<HTMLInputElement>(null);
|
||||
const onSubmit: SubmitHandler<ILoginCredentials> = (data) => {
|
||||
mutate(data);
|
||||
reset();
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Controller
|
||||
<InputGroup
|
||||
type="username"
|
||||
{...register("username", {
|
||||
required: {
|
||||
value: true,
|
||||
message: FormErrorMessages.required,
|
||||
},
|
||||
})}
|
||||
label="Username"
|
||||
className="mb-4 [&_input]:py-[15px]"
|
||||
placeholder="Enter your Username"
|
||||
name="username"
|
||||
control={control}
|
||||
render={({ field: username }) => (
|
||||
<InputGroup
|
||||
type="username"
|
||||
handleChange={(value) => username.onChange(value)}
|
||||
label="Username"
|
||||
className="mb-4 [&_input]:py-[15px]"
|
||||
placeholder="Enter your Username"
|
||||
name="username"
|
||||
icon={<UserIcon />}
|
||||
/>
|
||||
)}
|
||||
icon={<UserIcon />}
|
||||
/>
|
||||
<Controller
|
||||
|
||||
<InputGroup
|
||||
type={passwordFieldInputType}
|
||||
{...register("password", {
|
||||
required: {
|
||||
value: true,
|
||||
message: FormErrorMessages.required,
|
||||
},
|
||||
})}
|
||||
label="Password"
|
||||
className="mb-5 [&_input]:py-[15px]"
|
||||
placeholder="Enter your password"
|
||||
name="password"
|
||||
control={control}
|
||||
render={({ field: password }) => (
|
||||
<InputGroup
|
||||
type="password"
|
||||
handleChange={(value) => password.onChange(value)}
|
||||
label="Password"
|
||||
className="mb-5 [&_input]:py-[15px]"
|
||||
placeholder="Enter your password"
|
||||
name="password"
|
||||
icon={<PasswordIcon />}
|
||||
icon={
|
||||
<PasswordIcon
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
setPasswordFieldInputType(
|
||||
passwordFieldInputType === "password" ? "text" : "password",
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="mb-4.5">
|
||||
@@ -53,9 +68,6 @@ export default function SigninWithPassword() {
|
||||
className="flex w-full cursor-pointer items-center justify-center gap-2 rounded-lg bg-primary p-4 font-medium text-white transition hover:bg-opacity-90"
|
||||
>
|
||||
Sign In
|
||||
{/* {loading && (
|
||||
<span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-solid border-white border-t-transparent dark:border-primary dark:border-t-transparent" />
|
||||
)} */}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -11,10 +11,11 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { LogOutIcon, SettingsIcon, UserIcon } from "./icons";
|
||||
import { useUser } from "@/contexts";
|
||||
|
||||
export function UserInfo() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const { logout } = useUser();
|
||||
const USER = {
|
||||
name: "John Smith",
|
||||
email: "johnson@nextadmin.com",
|
||||
@@ -106,7 +107,11 @@ export function UserInfo() {
|
||||
<div className="p-2 text-base text-[#4B5563] dark:text-dark-6">
|
||||
<button
|
||||
className="flex w-full items-center gap-2.5 rounded-lg px-2.5 py-[9px] hover:bg-gray-2 hover:text-dark dark:hover:bg-dark-3 dark:hover:text-white"
|
||||
onClick={() => setIsOpen(false)}
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
logout();
|
||||
|
||||
}}
|
||||
>
|
||||
<LogOutIcon />
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export function Sidebar() {
|
||||
});
|
||||
});
|
||||
});
|
||||
}, [pathname]);
|
||||
}, [pathname, expandedItems]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -50,11 +50,14 @@ const CompaniesTable = ({}: IProps) => {
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{allCompanies.map((company) =>
|
||||
{allCompanies.map((company, index) =>
|
||||
!company ? (
|
||||
<h6>No Data was Found</h6>
|
||||
<h6 key={index}>No Data was Found</h6>
|
||||
) : (
|
||||
<TableRow key={company.id} className="odd:bg-gray-2 dark:odd:bg-gray-7">
|
||||
<TableRow
|
||||
key={company.id}
|
||||
className="odd:bg-gray-2 dark:odd:bg-gray-7"
|
||||
>
|
||||
<TableHead className="text-start" colSpan={100}>
|
||||
{company.name}
|
||||
</TableHead>
|
||||
|
||||
@@ -46,8 +46,8 @@ const CustomersTable = () => {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{allCustomers.map((customer, index) => (
|
||||
<TableRow key={index}>
|
||||
{allCustomers.map((customer) => (
|
||||
<TableRow key={customer.id}>
|
||||
<TableCell colSpan={100}>{customer.full_name}</TableCell>
|
||||
<TableCell colSpan={100}>{customer.email}</TableCell>
|
||||
<TableCell colSpan={100}>{customer.created_at}</TableCell>
|
||||
@@ -55,7 +55,9 @@ const CustomersTable = () => {
|
||||
<TableCell colSpan={100}>{customer.status}</TableCell>
|
||||
<TableCell colSpan={100}>
|
||||
{customer.companies?.length ? (
|
||||
customer.companies.map((c) => <span>{c.name}, </span>)
|
||||
customer.companies.map((c) => (
|
||||
<span key={c.id}>{c.name}, </span>
|
||||
))
|
||||
) : (
|
||||
<span>None</span>
|
||||
)}
|
||||
|
||||
@@ -33,8 +33,10 @@ const TendersTable = () => {
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{allTenders.map((item) => {
|
||||
return (
|
||||
{allTenders.map((item, index) =>
|
||||
!item ? (
|
||||
<h6 key={index}>No tenders were found</h6>
|
||||
) : (
|
||||
<TableRow
|
||||
key={item.id}
|
||||
className="odd:bg-gray-2 dark:odd:bg-gray-7"
|
||||
@@ -49,8 +51,8 @@ const TendersTable = () => {
|
||||
{item.status}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
@@ -49,11 +49,11 @@ const useTenderListPresenter = () => {
|
||||
const handleFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value);
|
||||
};
|
||||
const onConfirmDelete = () => {
|
||||
if (currentTender) {
|
||||
deleteTender(currentTender.id);
|
||||
}
|
||||
};
|
||||
// const onConfirmDelete = () => {
|
||||
// if (currentTender) {
|
||||
// deleteTender(currentTender.id);
|
||||
// }
|
||||
// };
|
||||
|
||||
const allTenders = data?.pages.flatMap((page) => page.data.tenders) ?? [];
|
||||
return {
|
||||
@@ -61,7 +61,6 @@ const useTenderListPresenter = () => {
|
||||
setCurrentTender,
|
||||
search,
|
||||
handleFilterChange,
|
||||
onConfirmDelete,
|
||||
allTenders,
|
||||
error,
|
||||
isPending,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// src/contexts/UserContext.tsx
|
||||
"use client";
|
||||
|
||||
import React, {
|
||||
@@ -15,6 +14,7 @@ import Cookies from "js-cookie";
|
||||
import { z } from "zod";
|
||||
import { UserSchema, ILoginResponse } from "../lib/api/types";
|
||||
import { COOKIE_KEYS } from "../lib/shared/cookies";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
type User = z.infer<typeof UserSchema>;
|
||||
|
||||
@@ -37,7 +37,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
try {
|
||||
const tokenFromCookie = Cookies.get(COOKIE_KEYS.access_token);
|
||||
@@ -88,7 +88,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
setAuthState,
|
||||
logout,
|
||||
}),
|
||||
[user, accessToken, isLoading, setAuthState, logout]
|
||||
[user, accessToken, isLoading, setAuthState, logout],
|
||||
);
|
||||
|
||||
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
|
||||
|
||||
@@ -27,6 +27,7 @@ export const useCustomersInfiniteQuery = (params?: Record<string, any>) => {
|
||||
initialPageParam: 0,
|
||||
|
||||
getNextPageParam: (lastPage) => {
|
||||
if (!lastPage.meta) return;
|
||||
const { offset, limit, total } = lastPage.meta;
|
||||
|
||||
const nextOffset = offset + limit;
|
||||
@@ -42,6 +43,13 @@ export const useGetAllCustomers = () => {
|
||||
queryFn: () => customersService.getCustomers(),
|
||||
});
|
||||
};
|
||||
export const useGetCustomersDetails = (id: string) => {
|
||||
const queryKey = useMemo(() => ["GET ALL CUSTOMERS", id], [id]);
|
||||
return useQuery({
|
||||
queryKey,
|
||||
queryFn: () => customersService.customerDetails(id),
|
||||
});
|
||||
};
|
||||
export const useAssignCompany = (id: string, successCallback?: () => void) => {
|
||||
const queryClient = useQueryClient();
|
||||
const mutationKey = useMemo(
|
||||
|
||||
@@ -49,6 +49,10 @@ export const customersService = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
customerDetails: async (id: string) => {
|
||||
const response = await api.get(API_ENDPOINTS.CUSTOMERS.DETAILS(id));
|
||||
return response.data;
|
||||
},
|
||||
assignCompanyToCustomer: async ({
|
||||
credentials,
|
||||
id,
|
||||
|
||||
@@ -24,6 +24,6 @@ const TenderSchema = z.object({
|
||||
title: z.string(),
|
||||
});
|
||||
export const TendersResponse = z.object({
|
||||
companies: z.array(TenderSchema).or(z.null()),
|
||||
tenders: z.array(TenderSchema).or(z.null()),
|
||||
});
|
||||
export type TTenderResponse = z.infer<typeof TendersResponse>;
|
||||
|
||||
Reference in New Issue
Block a user