Skip to content

Commit

Permalink
[ADD] - Added tutor modification panel
Browse files Browse the repository at this point in the history
  • Loading branch information
nulzo committed Nov 19, 2023
1 parent 413cf40 commit 45e8387
Show file tree
Hide file tree
Showing 24 changed files with 517 additions and 360 deletions.
4 changes: 3 additions & 1 deletion backend/api/endpoints/ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def sanitize(self, querystring: str) -> str:

def get(self, request: Request) -> Response:
tickets = Ticket.generic.all()
print(tickets)
querystring = self.get_querystring(request=request)
if len(querystring) > 0:
if department := querystring.get("department"):
Expand All @@ -42,6 +41,9 @@ def get(self, request: Request) -> Response:
if active := querystring.get("active"):
tickets = tickets.filter(is_active=active.capitalize())

if status := querystring.get("status"):
tickets = tickets.filter(status=status.upper())

serializer = TicketSerializer(tickets, many=True)
return Response(serializer.data)

Expand Down
4 changes: 2 additions & 2 deletions backend/api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Generated by Django 4.2.7 on 2023-11-18 21:51
# Generated by Django 4.2.7 on 2023-11-18 23:32

from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager
from django.db import migrations, models


class Migration(migrations.Migration):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 4.2.7 on 2023-11-19 00:46

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("api", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="ticket",
name="student",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="student_ticket",
to="api.user",
),
),
migrations.AlterField(
model_name="ticket",
name="tutor",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="tutor_ticket",
to="api.user",
),
),
]
2 changes: 2 additions & 0 deletions backend/api/models/ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ class Ticket(models.Model):
issue = models.ForeignKey("api.Issues", null=True, on_delete=models.PROTECT)
student = models.ForeignKey(
"api.User",
null=True,
on_delete=models.PROTECT,
related_name="student_ticket",
blank=True,
)
tutor = models.ForeignKey(
"api.User",
null=True,
on_delete=models.PROTECT,
related_name="tutor_ticket",
blank=True,
Expand Down
8 changes: 5 additions & 3 deletions backend/base/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
ALLOWED_HOSTS: list[str] = ["*", "http://localhost", "localhost"]

CSRF_TRUSTED_ORIGINS = [
'http://localhost', 'http://localhost:80', 'http://localhost:4200', 'http://localhost:6969'
"http://localhost",
"http://localhost:80",
"http://localhost:4200",
"http://localhost:6969",
]

INSTALLED_APPS = [
Expand Down Expand Up @@ -62,8 +65,7 @@
CORS_ALLOW_ALL_ORIGINS = True

ASGI_APPLICATION = "api.routing.application"
CHANNEL_LAYERS = {"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"}}
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}

MESSAGE_STORAGE = "django.contrib.messages.storage.cookie.CookieStorage"

Expand Down
24 changes: 11 additions & 13 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image" href="/UNO.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CSLC Tutoring Portal</title>
</head>

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image" href="/UNO.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CSLC Tutoring Portal</title>
</head>

<body class="overscroll-none">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
<body class="overscroll-none">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
34 changes: 19 additions & 15 deletions frontend/src/API/courses/courseRequests.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import axios from "axios"
import axios from "axios";

function getCookie(name: string) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(";").shift();
}

export function useMutateCourse(data: any) {

Check warning on line 9 in frontend/src/API/courses/courseRequests.ts

View workflow job for this annotation

GitHub Actions / run-npm

Unexpected any. Specify a different type
const csrftoken = getCookie('csrftoken');
return axios.post("/api/courses/", {
const csrftoken = getCookie("csrftoken");
return axios
.post(
"/api/courses/",
{
course_department: data.course_department,
course_name: data.course_name,
course_id: data.course_id,
course_code: data.course_code,
},
{
headers: {
'X-CSRFToken': csrftoken
}
})
.then(res => res.data);
},
{
headers: {
"X-CSRFToken": csrftoken,
},
},
)
.then((res) => res.data);
}

export function useFetchCourse() {
return axios.get("/api/courses/");
}
return axios.get("/api/courses/");
}
37 changes: 15 additions & 22 deletions frontend/src/API/tickets/ticketRequests.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
import axios from "axios"
import axios from "axios";

export function createTicket(data: any) {

Check warning on line 3 in frontend/src/API/tickets/ticketRequests.ts

View workflow job for this annotation

GitHub Actions / run-npm

Unexpected any. Specify a different type
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
const csrftoken = getCookie('csrftoken');
return axios.post("/api/tickets/", {
name: data.student_name,
title: data.title,
description: data.body,
professor: data.professor,
course: data.section,
issue: data.issue,
},
{
headers: {
'X-CSRFToken': csrftoken
}
})
.then(res => res.data);
}
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(";").shift();
}
const csrftoken = getCookie("csrftoken");
return axios
.post("/api/tickets/", data, {
headers: {
"X-CSRFToken": csrftoken,
},
})
.then((res) => res.data);
}
37 changes: 37 additions & 0 deletions frontend/src/components/forms/ModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Moon, Sun } from "lucide-react";

import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useTheme } from "@/components/forms/ThemeProvider";

export function ModeToggle() {
const { setTheme } = useTheme();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
73 changes: 73 additions & 0 deletions frontend/src/components/forms/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { createContext, useContext, useEffect, useState } from "react";

type Theme = "dark" | "light" | "system";

type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
};

type ThemeProviderState = {
theme: Theme;
setTheme: (theme: Theme) => void;
};

const initialState: ThemeProviderState = {
theme: "system",
setTheme: () => null,
};

const ThemeProviderContext = createContext<ThemeProviderState>(initialState);

export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
);

useEffect(() => {
const root = window.document.documentElement;

root.classList.remove("light", "dark");

if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";

root.classList.add(systemTheme);
return;
}

root.classList.add(theme);
}, [theme]);

const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};

return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}

export const useTheme = () => {
const context = useContext(ThemeProviderContext);

if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");

return context;
};
Loading

0 comments on commit 45e8387

Please sign in to comment.