[1.0.4] #29

Merged
kailasdevdas merged 7 commits from dev into main 2026-05-14 03:53:43 +00:00
7 changed files with 237 additions and 86 deletions
@@ -84,7 +84,11 @@ export const createAppointment = async (req, res) => {
<tr> <tr>
<td style="padding: 8px 0;"><b>Date:</b></td> <td style="padding: 8px 0;"><b>Date:</b></td>
<td style="padding: 8px 0;"> <td style="padding: 8px 0;">
${new Date(date).toLocaleDateString()} ${new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "long",
year: "numeric",
})}
</td> </td>
</tr> </tr>
</table> </table>
@@ -143,18 +147,51 @@ export const getAppointments = async (req, res) => {
const page = parseInt(req.query.page) || 1; const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10; const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
const { date, search } = req.query;
const { date, startDate, endDate, search } = req.query;
const where = {}; const where = {};
if (date) { const hasSingleDate = date && date.trim() !== "";
const hasRange =
(startDate && startDate.trim() !== "") ||
(endDate && endDate.trim() !== "");
if (hasSingleDate) {
const start = new Date(date); const start = new Date(date);
start.setHours(0, 0, 0, 0);
const end = new Date(date); const end = new Date(date);
end.setDate(end.getDate() + 1); end.setHours(23, 59, 59, 999);
where.date = { gte: start, lt: end };
where.date = {
gte: start,
lte: end,
};
} }
if (search) { if (!hasSingleDate && hasRange) {
const dateFilter = {};
if (startDate && startDate.trim() !== "") {
const start = new Date(startDate);
start.setHours(0, 0, 0, 0);
dateFilter.gte = start;
}
if (endDate && endDate.trim() !== "") {
const end = new Date(endDate);
end.setHours(23, 59, 59, 999);
dateFilter.lte = end;
}
where.date = dateFilter;
}
if (search && search.trim() !== "") {
where.OR = [ where.OR = [
{ name: { contains: search, mode: "insensitive" } }, { name: { contains: search, mode: "insensitive" } },
{ mobileNumber: { contains: search } }, { mobileNumber: { contains: search } },
@@ -165,24 +202,39 @@ export const getAppointments = async (req, res) => {
const [appointments, total] = await Promise.all([ const [appointments, total] = await Promise.all([
prisma.appointment.findMany({ prisma.appointment.findMany({
where, where,
include: { doctor: true, department: true }, include: {
orderBy: { createdAt: "desc" }, doctor: true,
department: true,
},
orderBy: {
createdAt: "desc",
},
skip, skip,
take: limit, take: limit,
}), }),
prisma.appointment.count({ where }),
prisma.appointment.count({
where,
}),
]); ]);
res.status(200).json({ res.status(200).json({
success: true, success: true,
data: appointments, data: appointments,
pagination: { total, page, limit, totalPages: Math.ceil(total / limit) }, pagination: {
total,
page,
limit,
totalPages: Math.ceil(total / limit),
},
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
res
.status(500) res.status(500).json({
.json({ success: false, message: "Failed to fetch appointments" }); success: false,
message: "Failed to fetch appointments",
});
} }
}; };
+6 -1
View File
@@ -273,6 +273,11 @@ export const updateDoctor = async (req, res) => {
}, },
}); });
const hasTimingData = departments?.some(
(dep) => dep.timing && Object.keys(dep.timing).length > 0,
);
if (departments && Array.isArray(departments) && hasTimingData) {
const oldRelations = await prisma.doctorDepartment.findMany({ const oldRelations = await prisma.doctorDepartment.findMany({
where: {doctorId: doctor.id}, where: {doctorId: doctor.id},
}); });
@@ -304,12 +309,12 @@ export const updateDoctor = async (req, res) => {
if (dep.timing) { if (dep.timing) {
const {id, doctorDepartmentId, createdAt, updatedAt, ...cleanTiming} = const {id, doctorDepartmentId, createdAt, updatedAt, ...cleanTiming} =
dep.timing; dep.timing;
await prisma.doctorTiming.create({ await prisma.doctorTiming.create({
data: {doctorDepartmentId: newDD.id, ...cleanTiming}, data: {doctorDepartmentId: newDD.id, ...cleanTiming},
}); });
} }
} }
}
res res
.status(200) .status(200)
+4
View File
@@ -4,12 +4,16 @@ export const getAppointmentsApi = async (
page = 1, page = 1,
limit = 10, limit = 10,
date = "", date = "",
startDate = "",
endDate = "",
search = "", search = "",
) => { ) => {
const params = new URLSearchParams({ const params = new URLSearchParams({
page: String(page), page: String(page),
limit: String(limit), limit: String(limit),
...(date && { date }), ...(date && { date }),
...(startDate && { startDate }),
...(endDate && { endDate }),
...(search && { search }), ...(search && { search }),
}); });
const res = await apiClient.get(`/appointments/getall?${params}`); const res = await apiClient.get(`/appointments/getall?${params}`);
+3 -1
View File
@@ -8,8 +8,10 @@ export interface Doctor {
designation?: string; designation?: string;
workingStatus?: string; workingStatus?: string;
qualification?: string; qualification?: string;
isActive: boolean;
globalSortOrder: number;
departments: { departments?: {
departmentId: string; departmentId: string;
timing?: { timing?: {
monday?: string; monday?: string;
+54 -3
View File
@@ -40,7 +40,8 @@ export default function AppointmentPage() {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [filterDoctor, setFilterDoctor] = useState(""); const [filterDoctor, setFilterDoctor] = useState("");
const [filterDate, setFilterDate] = useState(""); const [filterDate, setFilterDate] = useState("");
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const [viewOpen, setViewOpen] = useState(false); const [viewOpen, setViewOpen] = useState(false);
const [viewData, setViewData] = useState<any>(null); const [viewData, setViewData] = useState<any>(null);
@@ -56,6 +57,8 @@ export default function AppointmentPage() {
currentPage, currentPage,
itemsPerPage, itemsPerPage,
filterDate, filterDate,
startDate,
endDate,
searchText, searchText,
); );
setAppointments(res?.data || []); setAppointments(res?.data || []);
@@ -66,7 +69,7 @@ export default function AppointmentPage() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [currentPage, itemsPerPage, filterDate, searchText]); }, [currentPage, itemsPerPage, filterDate, startDate, endDate, searchText]);
useEffect(() => { useEffect(() => {
fetchAll(); fetchAll();
@@ -116,7 +119,11 @@ export default function AppointmentPage() {
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4"> <div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
<h1 className="text-3xl font-bold">Appointments</h1> <h1 className="text-3xl font-bold">Appointments</h1>
<div className="flex flex-wrap gap-3"> <div className="flex flex-wrap gap-4 items-end">
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-muted-foreground">
Search
</label>
<Input <Input
placeholder="Search name / phone..." placeholder="Search name / phone..."
value={searchText} value={searchText}
@@ -126,7 +133,12 @@ export default function AppointmentPage() {
}} }}
className="w-[220px] text-base" className="w-[220px] text-base"
/> />
</div>
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-muted-foreground">
Date
</label>
<Input <Input
type="date" type="date"
value={filterDate} value={filterDate}
@@ -135,8 +147,46 @@ export default function AppointmentPage() {
setCurrentPage(1); setCurrentPage(1);
}} }}
className="w-[160px] text-base" className="w-[160px] text-base"
disabled={!!startDate || !!endDate}
/> />
</div>
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-muted-foreground">
From
</label>
<Input
type="date"
value={startDate}
onChange={(e) => {
setStartDate(e.target.value);
setCurrentPage(1);
}}
className="w-[160px] text-base"
disabled={!!filterDate}
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-muted-foreground">
To
</label>
<Input
type="date"
value={endDate}
onChange={(e) => {
setEndDate(e.target.value);
setCurrentPage(1);
}}
className="w-[160px] text-base"
disabled={!!filterDate}
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-muted-foreground">
Rows
</label>
<select <select
value={itemsPerPage} value={itemsPerPage}
onChange={(e) => { onChange={(e) => {
@@ -149,6 +199,7 @@ export default function AppointmentPage() {
<option value={10}>10 / page</option> <option value={10}>10 / page</option>
<option value={20}>20 / page</option> <option value={20}>20 / page</option>
</select> </select>
</div>
<Button <Button
variant="outline" variant="outline"
+33 -2
View File
@@ -10,6 +10,7 @@ import Table from "@editorjs/table";
import CodeTool from "@editorjs/code"; import CodeTool from "@editorjs/code";
import Embed from "@editorjs/embed"; import Embed from "@editorjs/embed";
import Delimiter from "@editorjs/delimiter"; import Delimiter from "@editorjs/delimiter";
import axios from "axios";
import { import {
createBlogApi, createBlogApi,
@@ -23,6 +24,7 @@ import {Input} from "@/components/ui/input";
import {Button} from "@/components/ui/button"; import {Button} from "@/components/ui/button";
export default function BlogEditorPage() { export default function BlogEditorPage() {
const baseURL = import.meta.env.VITE_API_URL;
const {id} = useParams(); const {id} = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -79,12 +81,41 @@ export default function BlogEditorPage() {
config: { config: {
uploader: { uploader: {
uploadByFile: async (file: File) => { uploadByFile: async (file: File) => {
const res = await uploadImageApi(file); if (file.size > 5 * 1024 * 1024) {
alert("File is too large (Max 5MB)");
return {success: 0, file: {url: ""}};
}
const formData = new FormData();
formData.append("file", file);
formData.append("folderPath", "/blog");
try {
const response = await axios.post(
`${baseURL}/upload`,
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
},
);
return { return {
success: 1, success: 1,
file: {url: res.file.url}, file: {url: response.data.fileUrl},
}; };
} catch (e: any) {
console.error("EditorJS Image Upload Error:", e);
const errorMessage =
e.response?.data?.error || e.message || "Upload failed";
alert(`Upload Error: ${errorMessage}`);
return {
success: 0,
file: {url: ""},
};
}
}, },
}, },
}, },
+8 -2
View File
@@ -158,8 +158,14 @@ export default function DoctorPage() {
const handleToggleStatus = async (doc: any) => { const handleToggleStatus = async (doc: any) => {
try { try {
const updatedDoc = { ...doc, isActive: !doc.isActive }; const newStatus = !doc.isActive;
await updateDoctorApi(doc.doctorId, updatedDoc);
const payload = {
isActive: newStatus,
};
await updateDoctorApi(doc.doctorId, payload);
fetchAll(); fetchAll();
} catch (err) { } catch (err) {
console.error("Failed to update status", err); console.error("Failed to update status", err);