added xl, and doc file allow to upload and getSubscription plan api properly

This commit is contained in:
pramod.mahajan 2025-10-15 17:31:58 +05:30
parent 9648d1a98b
commit 6e89fbd680
9 changed files with 147 additions and 94 deletions

View File

@ -75,6 +75,15 @@ const AddPayment = ({ onClose }) => {
<DatePicker
name="paymentReceivedDate"
control={control}
minDate={
data?.clientSubmitedDate
? new Date(
new Date(data?.clientSubmitedDate).setDate(
new Date(data?.clientSubmitedDate).getDate() + 1
)
)
: null
}
maxDate={new Date()}
/>
{errors.paymentReceivedDate && (
@ -101,7 +110,9 @@ const AddPayment = ({ onClose }) => {
) : (
<>
<option value="">Select Payment Head</option>
{paymentTypes?.data?.sort((a, b) => a.name.localeCompare(b.name))?.map((type) => (
{paymentTypes?.data
?.sort((a, b) => a.name.localeCompare(b.name))
?.map((type) => (
<option key={type.id} value={type.id}>
{type.name}
</option>
@ -153,8 +164,8 @@ const AddPayment = ({ onClose }) => {
type="reset"
className="btn btn-label-secondary btn-sm mt-3"
onClick={() => {
handleClose()
onClose()
handleClose();
onClose();
}}
disabled={isPending}
>

View File

@ -164,6 +164,31 @@ const ManageCollection = ({ collectionId, onClose }) => {
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} className="p-0 text-start">
<div className="row px-md-1 px-0">
<div className="col-12 col-md-6 mb-2">
<Label className="form-label" required>
Select Project
</Label>
<select
className="form-select form-select-sm"
{...register("projectId")}
>
<option value="">Select Project</option>
{projectLoading ? (
<option>Loading...</option>
) : (
projectNames?.map((project) => (
<option key={project.id} value={project.id}>
{project.name}
</option>
))
)}
</select>
{errors.projectId && (
<small className="danger-text">
{errors.projectId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 mb-2">
<Label required>Title</Label>
<input
@ -215,7 +240,7 @@ const ManageCollection = ({ collectionId, onClose }) => {
)}
</div>
<div className="col-12 col-md-6 mb-2">
<Label required>Expected Date</Label>
<Label required>Expected Payment Date</Label>
<DatePicker
name="exceptedPaymentDate"
control={control}
@ -240,34 +265,10 @@ const ManageCollection = ({ collectionId, onClose }) => {
</small>
)}
</div>
<div className="col-12 col-md-6 mb-2">
<Label className="form-label" required>
Select Project
</Label>
<select
className="form-select form-select-sm"
{...register("projectId")}
>
<option value="">Select Project</option>
{projectLoading ? (
<option>Loading...</option>
) : (
projectNames?.map((project) => (
<option key={project.id} value={project.id}>
{project.name}
</option>
))
)}
</select>
{errors.projectId && (
<small className="danger-text">
{errors.projectId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 mb-2">
<Label htmlFor="basicAmount" className="form-label" required>
Amount
Basic Amount
</Label>
<input
type="number"
@ -336,12 +337,23 @@ const ManageCollection = ({ collectionId, onClose }) => {
<span className="text-muted d-block">
Click to select or click here to browse
</span>
<small className="text-muted">(PDF, JPG, PNG, max 5MB)</small>
<small className="text-muted">
(PDF, JPG, PNG,Doc,docx,xls,xlsx max 5MB)
</small>
<input
type="file"
id="attachments"
accept=".pdf,.jpg,.jpeg,.png"
accept="
.pdf,
.doc,
.docx,
.xls,
.xlsx,
.jpg,
.jpeg,
.png
"
multiple
style={{ display: "none" }}
{...register("attachments")}

View File

@ -3,11 +3,16 @@ import { z } from "zod";
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ALLOWED_TYPES = [
"application/pdf",
"application/doc",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"image/png",
"image/jpg",
"image/jpeg",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
];
export const newCollection = z.object({
title: z.string().trim().min(1, { message: "Title is required" }),
projectId: z.string().trim().min(1, { message: "Project is required" }),

View File

@ -32,6 +32,16 @@ export const useModal = (modalType) => {
return { isOpen, onOpen, onClose, onToggle };
};
export const useSubscription = (frequency) => {
return useQuery({
queryKey: ["subscriptionPlans", frequency],
queryFn: async () => {
debugger
const resp = await AuthRepository.getSubscription(frequency);
return resp.data;
},
});
};
// -------------------APIHook-------------------------------------
@ -85,8 +95,8 @@ export const useAuthModal = () => {
export const useLogout = () => {
const queryClient = useQueryClient();
const naviget = useNavigate()
const dispatch = useDispatch()
const naviget = useNavigate();
const dispatch = useDispatch();
return useMutation({
mutationFn: async () => {
@ -99,12 +109,12 @@ export const useLogout = () => {
},
onSuccess: (data) => {
queryClient.clear()
queryClient.clear();
removeSession();
dispatch(cacheProfileData(null))
dispatch(cacheProfileData(null));
// window.location.href = "/auth/login";
naviget("/auth/login",{replace:true})
naviget("/auth/login", { replace: true });
if (onSuccessCallBack) onSuccessCallBack();
},

View File

@ -206,8 +206,8 @@ const LandingPage = () => {
navigation={false}
modules={[EffectFlip, Autoplay, Pagination, Navigation]}
className="mySwiper"
onSlideChange={() => console.log("slide change")}
onSwiper={(swiper) => console.log(swiper)}
onSlideChange={() => {}}
onSwiper={(swiper) => {}}
>
<SwiperSlide>
<SwaperSlideContent

View File

@ -2,37 +2,28 @@ import React, { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import PlanCardSkeleton from "./PlanCardSkeleton";
import { useSubscription } from "../../hooks/useAuth";
const SubscriptionPlans = () => {
const [plans, setPlans] = useState([]);
const [frequency, setFrequency] = useState(1);
const { data, isLoading, isError, error } = useSubscription(frequency);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchPlans = async () => {
try {
setLoading(true);
const res = await axios.get(
`http://localhost:5032/api/market/list/subscription-plan?frequency=${frequency}`,
{ headers: { "Content-Type": "application/json" } }
);
setPlans(res.data?.data || []);
} catch (err) {
console.error("Error fetching plans:", err);
} finally {
setLoading(false);
}
};
fetchPlans();
}, [frequency]);
const frequencyLabel = (freq) => {
switch (freq) {
case 0: return "1 mo";
case 1: return "3 mo";
case 2: return "6 mo";
case 3: return "1 yr";
default: return "mo";
case 0:
return "1 mo";
case 1:
return "3 mo";
case 2:
return "6 mo";
case 3:
return "1 yr";
default:
return "mo";
}
};
@ -41,38 +32,49 @@ const SubscriptionPlans = () => {
{/* Frequency Switcher */}
<div className="text-center mb-4">
<div className="btn-group" role="group" aria-label="Plan frequency">
{["Monthly", "Quarterly", "Half-Yearly", "Yearly"].map((label, idx) => (
{["Monthly", "Quarterly", "Half-Yearly", "Yearly"].map(
(label, idx) => (
<button
key={idx}
type="button"
className={`btn btn-${frequency === idx ? "primary" : "outline-secondary"}`}
className={`btn btn-${
frequency === idx ? "primary" : "outline-secondary"
}`}
onClick={() => setFrequency(idx)}
>
{label}
</button>
))}
)
)}
</div>
</div>
{/* Cards */}
<div className="row g-4 mt-10">
{loading ? (
{isLoading ? (
// Show 3 skeletons
<>
<PlanCardSkeleton />
<PlanCardSkeleton />
<PlanCardSkeleton />
</>
) : plans.length === 0 ? (
) : data.length === 0 ? (
<div className="text-center">No plans found</div>
) : isError ? (
<div className="text-start bg-light">
<p>{error.message}</p>
<p>{error.name}</p>
</div>
) : (
plans.map((plan) => (
data.map((plan) => (
<div key={plan.id} className="col-xl-4 col-lg-6 col-md-6">
<div className="card h-100 shadow-lg border-0 p-3 text-center p-10">
{/* Header */}
<div className="mb-3">
<i className="bx bxs-package text-primary fs-1 mb-2"></i>
<p className="card-title fs-3 fw-bold mb-1">{plan.planName}</p>
<p className="card-title fs-3 fw-bold mb-1">
{plan.planName}
</p>
<p className="text-muted mb-0 fs-5">{plan.description}</p>
</div>
@ -80,7 +82,9 @@ const SubscriptionPlans = () => {
<div className="mb-3">
<h4 className="fw-semibold mt-auto mb-0 fs-3">
{plan.currency?.symbol} {plan.price}
<small className="text-muted ms-1">/ {frequencyLabel(frequency)}</small>
<small className="text-muted ms-1">
/ {frequencyLabel(frequency)}
</small>
</h4>
</div>
@ -133,7 +137,6 @@ const SubscriptionPlans = () => {
))
)}
</div>
</div>
);
};

View File

@ -181,7 +181,7 @@ const CollectionPage = () => {
<ConfirmModal
type="success"
header="Payment Successful Received"
message="Your payment has been processed successfully. Do you want to continue?"
message="Payment has been recored successfully."
isOpen={processedPayment?.isOpen}
loading={isPending}
onSubmit={() => handleMarkedPayment(processedPayment?.invoiceId)}

View File

@ -10,6 +10,7 @@ const AuthRepository = {
verifyOTP: (data) => api.postPublic("/api/auth/login-otp", data),
register: (data) => api.postPublic("/api/auth/register", data),
sendMail: (data) => api.postPublic("/api/auth/sendmail", data),
getSubscription:(frequency)=> api.getPublic(`/api/market/list/subscription-plan?frequency=${frequency}`),
// Protected routes (require auth token)
logout: (data) => api.post("/api/auth/logout", data),
@ -19,6 +20,8 @@ const AuthRepository = {
selectTenant: (tenantId) => api.post(`/api/Auth/select-tenant/${tenantId}`),
getTenantList: () => api.get("/api/Auth/get/user/tenants"),
//
};
export default AuthRepository;

View File

@ -72,7 +72,9 @@ axiosClient.interceptors.response.use(
if (status === 401 && !isRefreshRequest) {
originalRequest._retry = true;
const refreshToken = localStorage.getItem("refreshToken") || sessionStorage.getItem("refreshToken");
const refreshToken =
localStorage.getItem("refreshToken") ||
sessionStorage.getItem("refreshToken");
if (
!refreshToken ||
@ -87,7 +89,9 @@ axiosClient.interceptors.response.use(
try {
// Refresh token call
const res = await axiosClient.post("/api/Auth/refresh-token", {
token: localStorage.getItem("jwtToken") || sessionStorage.getItem("jwtToken"),
token:
localStorage.getItem("jwtToken") ||
sessionStorage.getItem("jwtToken"),
refreshToken,
});
@ -144,6 +148,11 @@ export const api = {
headers: { ...customHeaders },
authRequired: false,
}),
getPublic: (url, data = {}, customHeaders = {}) =>
apiRequest("get", url, data, {
headers: { ...customHeaders },
authRequired: false,
}),
// Authenticated routes
get: (url, params = {}, customHeaders = {}) =>