"use client"
import { useState, useEffect } from "react"
import { Outlet, useLocation, useNavigate } from "react-router-dom"
import { Layout, Menu, Button, Drawer, Avatar, Typography, Dropdown } from "antd"
import {
HomeOutlined,
BookOutlined,
UserOutlined,
ReadOutlined,
ClockCircleOutlined,
BankOutlined,
MenuOutlined,
LogoutOutlined,
SettingOutlined,
ScheduleOutlined,
} from "@ant-design/icons"
import { useAuth } from "../context/AuthContext"
const { Header, Sider, Content } = Layout
const { Text } = Typography
const DashboardLayout = () => {
const [collapsed, setCollapsed] = useState(false)
const [mobileView, setMobileView] = useState(false)
const [drawerVisible, setDrawerVisible] = useState(false)
const { school, logout } = useAuth()
const location = useLocation()
const navigate = useNavigate()
// Check if mobile view
useEffect(() => {
const handleResize = () => {
setMobileView(window.innerWidth < 768)
if (window.innerWidth >= 768) {
setDrawerVisible(false)
}
}
handleResize()
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
}, [])
// Get current selected key based on path
const getSelectedKey = () => {
const path = location.pathname
if (path.includes("/dashboard")) return "1"
if (path.includes("/classes")) return "2"
if (path.includes("/teachers")) return "3"
if (path.includes("/subjects")) return "4"
if (path.includes("/periods")) return "5"
if (path.includes("/school")) return "6"
return "1"
}
const menuItems = [
{
key: "1",
icon: <HomeOutlined />,
label: "Dashboard",
onClick: () => navigate("/dashboard"),
},
{
key: "2",
icon: <BookOutlined />,
label: "Classes",
onClick: () => navigate("/classes"),
},
{
key: "3",
icon: <UserOutlined />,
label: "Teachers",
onClick: () => navigate("/teachers"),
},
{
key: "4",
icon: <ReadOutlined />,
label: "Subjects",
onClick: () => navigate("/subjects"),
},
{
key: "5",
icon: <ClockCircleOutlined />,
label: "Periods",
onClick: () => navigate("/periods"),
},
{
key: "6",
icon: <ScheduleOutlined />,
label: "Schedules",
onClick: () => navigate("/schedules"),
},
{
key: "7",
icon: <BankOutlined />,
label: "School Profile",
onClick: () => navigate("/school"),
},
]
const userMenuItems = [
{
key: "1",
icon: <SettingOutlined />,
label: "School Profile",
onClick: () => navigate("/school"),
},
{
key: "2",
icon: <LogoutOutlined />,
label: "Logout",
onClick: logout,
},
]
const SidebarContent = () => (
<div className="h-full flex flex-col">
<div className="h-16 flex items-center justify-center border-b border-gray-200">
<div className="flex items-center gap-2 px-4">
<BankOutlined className="text-xl text-blue-600" />
{!collapsed && <span className="text-sm">School Management System</span>}
</div>
</div>
<Menu
theme="light"
mode="inline"
defaultSelectedKeys={[getSelectedKey()]}
selectedKeys={[getSelectedKey()]}
items={menuItems}
className="flex-1 border-r-0"
/>
</div>
)
return (
<Layout className="min-h-screen">
{/* Sidebar for desktop */}
{!mobileView && (
<Sider
collapsible
collapsed={collapsed}
onCollapse={setCollapsed}
width={240}
className="border-r border-gray-200"
>
<SidebarContent />
</Sider>
)}
{/* Drawer for mobile */}
{mobileView && (
<Drawer
placement="left"
onClose={() => setDrawerVisible(false)}
open={drawerVisible}
style={{ padding: 0 }}
width={240}
>
<SidebarContent />
</Drawer>
)}
<Layout>
<Header className="bg-white px-4 flex items-center justify-between border-b border-gray-200">
{mobileView && (
<Button type="text" icon={<MenuOutlined />} onClick={() => setDrawerVisible(true)} className="mr-3" />
)}
<div className="flex-1" />
<Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
<div className="flex items-center cursor-pointer">
<Avatar icon={<UserOutlined />} className="bg-blue-600" />
{!mobileView && (
<div className="ml-2">
<Text strong>{school?.name}</Text>
</div>
)}
</div>
</Dropdown>
</Header>
<Content className="p-6">
<Outlet />
</Content>
</Layout>
</Layout>
)
}
export default DashboardLayout
i want school profile in last bootom attached
I'll modify the DashboardLayout to place the School Profile at the bottom of the sidebar:
I've updated the DashboardLayout component to place the School Profile at the bottom of the sidebar:
menuItems arraymt-auto to push this section to the bottom of the sidebarThis change ensures that:
The School Profile is now attached to the bottom of the sidebar, creating a clear visual hierarchy between the main navigation items and the profile settings.
first drop down waht class when select chose class and table show add a schedule tab class wise like onew week 6 day school and period from api table style when click if not subject and teacher selected and subject select and choose after all done submit api for add and when get class chedule show import Schedule from "../models/Schedule.js";
export const createSchedule = async (req, res) => {
try {
const { schedule } = req.body;
const newSchedules = await Schedule.insertMany(schedule);
res.status(200).json({ schedules: newSchedules });
} catch (error) {
res.status(500).json({ error: 'Error saving schedule' });
}
};
i have only this code import mongoose from "mongoose";
const scheduleSchema = new mongoose.Schema({
schoolId: { type: mongoose.Schema.Types.ObjectId, ref: 'School', required: true },
classId: { type: mongoose.Schema.Types.ObjectId, ref: 'Class', required: true },
teacherId: { type: mongoose.Schema.Types.ObjectId, ref: 'Teacher', required: true },
subjectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Subject', required: true },
periodId: { type: mongoose.Schema.Types.ObjectId, ref: 'Period', required: true },
day: {
type: String,
enum: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
required: true
}
}, { timestamps: true });
const Schedule = mongoose.model('Schedule', scheduleSchema);
export default Schedule;
add a cotroller s and schedule add and apply best logic
code of scheule frontend also backend controller and routes import express from 'express';
import {
createSchedule,
} from '../controllers/scheduleController.js';
import authMiddleware from '../middleware/token.js';
const router = express.Router();
router.post('/', authMiddleware, createSchedule);
export default router;
I'll create a complete schedule management system for your school application. Let's start with the frontend components and then implement the backend controllers.
I've created a complete schedule management system for your school application. Here's what I've implemented:
Schedules.jsx: The main page component that:
ViewSchedule.jsx: A component that:
CreateSchedule.jsx: A component that:
scheduleController.js: Contains functions for:
scheduleRoutes.js: Defines the API routes for:
This implementation follows best practices for React and provides a user-friendly interface for managing school schedules.
"use client"
import { useState, useEffect } from "react"
import { Card, Select, Button, message, Tabs } from "antd"
import { PlusOutlined } from "@ant-design/icons"
import { apiClient } from "../../utils/apiClient"
import PageHeader from "../../components/common/PageHeader"
import LoadingSpinner from "../../components/common/LoadingSpinner"
import ViewSchedule from "./ViewSchedule"
import CreateSchedule from "./CreateSchedule"
const { Option } = Select
const Schedules = () => {
const [classes, setClasses] = useState([])
const [selectedClass, setSelectedClass] = useState(null)
const [loading, setLoading] = useState(true)
const [scheduleLoading, setScheduleLoading] = useState(false)
const [schedules, setSchedules] = useState([])
const [activeTab, setActiveTab] = useState("view") // 'view' or 'create'
// Fetch classes on component mount
useEffect(() => {
const fetchClasses = async () => {
try {
const response = await apiClient.get("/classes")
setClasses(response.data)
setLoading(false)
} catch (error) {
console.error("Error fetching classes:", error)
message.error("Failed to fetch classes")
setLoading(false)
}
}
fetchClasses()
}, [])
// Fetch schedules when a class is selected
useEffect(() => {
if (selectedClass) {
fetchSchedules(selectedClass)
}
}, [selectedClass])
const fetchSchedules = async (classId) => {
setScheduleLoading(true)
try {
const response = await apiClient.get(/schedules?classId=${classId})
setSchedules(response.data)
} catch (error) {
console.error("Error fetching schedules:", error)
message.error("Failed to fetch schedules")
} finally {
setScheduleLoading(false)
}
}
const handleClassChange = (value) => {
setSelectedClass(value)
}
if (loading) {
return <LoadingSpinner />
}
return (
<div>
<PageHeader title="Class Schedules" />
<Card className="mb-6">
<div className="flex flex-col md:flex-row md:items-center gap-4">
<div className="w-full md:w-1/3">
<label className="block text-sm font-medium text-gray-700 mb-1">Select Class</label>
<Select
placeholder="Select a class"
onChange={handleClassChange}
className="w-full"
showSearch
optionFilterProp="children"
>
{classes.map((cls) => (
<Option key={cls._id} value={cls._id}>
{cls.name} - {cls.section}
</Option>
))}
</Select>
</div>
{selectedClass && (
<div className="mt-4 md:mt-0">
<Button icon={<PlusOutlined />} onClick={() => setActiveTab("create")}>
Create New Schedule
</Button>
</div>
)}
</div>
</Card>
{selectedClass && (
<Card>
<Tabs
activeKey={activeTab}
onChange={setActiveTab}
items={[
{
key: "view",
label: "View Schedule",
children: (
<ViewSchedule
classId={selectedClass}
schedules={schedules}
loading={scheduleLoading}
refreshSchedules={() => fetchSchedules(selectedClass)}
/>
),
},
{
key: "create",
label: "Create Schedule",
children: (
<CreateSchedule
classId={selectedClass}
refreshSchedules={() => fetchSchedules(selectedClass)}
onComplete={() => setActiveTab("view")}
/>
),
},
]}
/>
</Card>
)}
</div>
)
}
export default Schedules
"use client"
import { useState, useEffect } from "react"
import { Table, Empty, Spin, Button, Popconfirm, message } from "antd"
import { DeleteOutlined } from "@ant-design/icons"
import { apiClient } from "../../utils/apiClient"
const ViewSchedule = ({ classId, schedules, loading, refreshSchedules }) => {
const [periods, setPeriods] = useState([])
const [teachers, setTeachers] = useState([])
const [subjects, setSubjects] = useState([])
const [dataLoading, setDataLoading] = useState(true)
const [processedSchedule, setProcessedSchedule] = useState({})
const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
useEffect(() => {
const fetchData = async () => {
setDataLoading(true)
try {
const [periodsRes, teachersRes, subjectsRes] = await Promise.all([
apiClient.get("/periods"),
apiClient.get("/teachers"),
apiClient.get("/subjects"),
])
setPeriods(periodsRes.data)
setTeachers(teachersRes.data)
setSubjects(subjectsRes.data)
} catch (error) {
console.error("Error fetching data:", error)
message.error("Failed to fetch schedule data")
} finally {
setDataLoading(false)
}
}
fetchData()
}, [])
// Process schedules into a format suitable for display
useEffect(() => {
if (schedules.length && periods.length && teachers.length && subjects.length) {
const processed = {}
days.forEach((day) => {
processed[day] = {}
periods.forEach((period) => {
const schedule = schedules.find((s) => s.day === day && s.periodId === period._id && s.classId === classId)
if (schedule) {
const teacher = teachers.find((t) => t._id === schedule.teacherId)
const subject = subjects.find((s) => s._id === schedule.subjectId)
processed[day][period._id] = {
...schedule,
teacherName: teacher ? teacher.name : "Unknown",
subjectName: subject ? subject.name : "Unknown",
periodTime: `${period.start} - ${period.end}`,
}
}
})
})
setProcessedSchedule(processed)
}
}, [schedules, periods, teachers, subjects, classId])
const handleDeleteSchedule = async (scheduleId) => {
try {
await apiClient.delete(/schedules/${scheduleId})
message.success("Schedule deleted successfully")
refreshSchedules()
} catch (error) {
console.error("Error deleting schedule:", error)
message.error("Failed to delete schedule")
}
}
const columns = [
{
title: "Period / Day",
dataIndex: "period",
key: "period",
fixed: "left",
width: 150,
render: (text, record) => (
<div>
<div className="font-bold">{record.period}</div>
<div className="text-xs text-gray-500">{record.time}</div>
</div>
),
},
...days.map((day) => ({
title: day,
dataIndex: day,
key: day,
width: 180,
render: (cell) =>
cell ? (
<div>
<div className="font-medium">{cell.subjectName}</div>
<div className="text-xs text-gray-500">{cell.teacherName}</div>
<div className="mt-1">
<Popconfirm
title="Delete this schedule?"
description="Are you sure you want to delete this schedule?"
onConfirm={() => handleDeleteSchedule(cell._id)}
okText="Yes"
okType="danger"
cancelText="No"
>
<Button type="text" danger icon={<DeleteOutlined />} size="small" />
</Popconfirm>
</div>
</div>
) : null,
})),
]
const dataSource = periods.map((period) => {
const row = {
key: period._id,
period: period._id,
periodName: period._id,
time: ${period.start} - ${period.end},
}
days.forEach((day) => {
if (processedSchedule[day] && processedSchedule[day][period._id]) {
row[day] = processedSchedule[day][period._id]
}
})
return row
})
if (loading || dataLoading) {
return <Spin size="large" className="flex justify-center my-12" />
}
if (!schedules.length) {
return <Empty description="No schedules found for this class" />
}
return (
<div className="overflow-x-auto">
<Table dataSource={dataSource} columns={columns} pagination={false} scroll={{ x: "max-content" }} bordered />
</div>
)
}
export default ViewSchedule
want to perios is one row and day in colums and no duplicate sir in same day and period and btn for edit and then edit delete btn show and good ui and wehn create or update show if sets periods
You can send messages while the preview loads