Merge branch 'main' of github.com:Central-University-IT-prod/PROD-Animulichki-SkillHub

This commit is contained in:
ITQ
2024-04-03 05:51:41 +03:00
10 changed files with 221 additions and 59 deletions
+7 -1
View File
@@ -5,6 +5,7 @@ from api.users.views import (
DownloadUsersFromExcelView, DownloadUsersFromExcelView,
RegisterUsersFromExcelView, RegisterUsersFromExcelView,
UserViewSet, UserViewSet,
UsersByEvent,
) )
app_name = "users" app_name = "users"
@@ -15,6 +16,11 @@ router.register("", UserViewSet)
urlpatterns = [ urlpatterns = [
path("", include(router.urls)), path("", include(router.urls)),
path(
"by-event/<event_id>/",
UsersByEvent.as_view(),
name="users-by-event",
),
path( path(
"upload/excel/<event_id>/", "upload/excel/<event_id>/",
RegisterUsersFromExcelView.as_view(), RegisterUsersFromExcelView.as_view(),
@@ -24,5 +30,5 @@ urlpatterns = [
"download/excel/<event_id>/", "download/excel/<event_id>/",
DownloadUsersFromExcelView.as_view(), DownloadUsersFromExcelView.as_view(),
name="excel-download", name="excel-download",
) ),
] ]
+16
View File
@@ -18,6 +18,22 @@ class UserViewSet(ModelViewSet):
serializer_class = UserSerializer serializer_class = UserSerializer
class UsersByEvent(APIView):
def get(self, _, event_id):
try:
event = Event.objects.get(pk=event_id)
except Event.DoesNotExist:
return Response(
{"error": "Event does not exist"},
status=status.HTTP_404_NOT_FOUND,
)
users = event.users.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
class RegisterUsersFromExcelView(APIView): class RegisterUsersFromExcelView(APIView):
def post(self, request, event_id): def post(self, request, event_id):
try: try:
@@ -5,6 +5,16 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 50px; gap: 50px;
margin-top: 40px;
height: 70vh; height: 70vh;
} }
.full-content{
padding-top: 80px;
}
.card{
max-width: 800px;
padding: 20px;
margin-bottom: 10px;
margin-left: 5px;
}
+42 -7
View File
@@ -8,23 +8,58 @@ import less from "./Admin.module.less";
import PlayerCard from "../../entities/PlayerCard/PlayerCard"; import PlayerCard from "../../entities/PlayerCard/PlayerCard";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CreateTeam from "../../widgets/CreateTeams/CreateTeams"; import CreateTeam from "../../widgets/CreateTeams/CreateTeams";
import { UserList } from "../AdminEventPage/AdminEventAPI";
import { useEffect, useState } from "react";
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "../../shared/ui/card"
const AdminPage = () => { const AdminPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [players, setPlayerList] = useState<Event[]>([]);
useEffect(() => {
var index = window.location.pathname.indexOf("/dash/admin/") + "/dash/admin/".length;
var result = window.location.pathname.substring(index);
UserList(result)
.then((data) => {
setPlayerList(data);
})
.catch((error) => {
console.error("Возникла ошибка с получением:", error);
});
}, []);
return ( return (
<ResizablePanelGroup direction="horizontal"> <ResizablePanelGroup className={less["full-content"]} direction="horizontal">
<ResizablePanel defaultSize={95} maxSize={95}> <ResizablePanel defaultSize={60} maxSize={60}>
<div className={less["main-admin"]}> <div className={less["main-admin"]}>
<Button>{t("EditTree")}</Button>
<CreateTeam /> <CreateTeam />
</div> </div>
</ResizablePanel> </ResizablePanel>
<ResizableHandle withHandle /> <ResizableHandle withHandle />
<ResizablePanel> <ResizablePanel>
<PlayerCard /> <h4>Members: {players.length}</h4>
<PlayerCard /> {players.map((event) => (
<PlayerCard /> <Card className={`${less["card"]} flex flex-row `}>
<PlayerCard /> <div className="flex flex-col">
<CardHeader className="p-0">
<CardTitle className="p-0">{`${event.first_name} | ${event.email}`}</CardTitle>
</CardHeader >
<CardContent className="p-0 mt-2">
<p>{t("skills")}:</p>
<p>{event.bio}</p>
</CardContent>
</div>
</Card>
))}
</ResizablePanel> </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>
); );
@@ -1,5 +1,5 @@
import { FormEvent } from "react"; import { FormEvent } from "react";
import { API_BASE, API_EVENT } from "../../app/APIurl"; import { API_BASE, API_EVENT, API_USERS } from "../../app/APIurl";
export const submitAddEvent = (e: FormEvent<HTMLFormElement>) => { export const submitAddEvent = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
@@ -56,6 +56,8 @@ export const eventList = () => {
console.error('Возникла ошибка с получением:', error); console.error('Возникла ошибка с получением:', error);
}); });
} }
//удалить ивент
export const deleteEvent = (id:string) => { export const deleteEvent = (id:string) => {
fetch(`${API_BASE}${API_EVENT}${id}`, { fetch(`${API_BASE}${API_EVENT}${id}`, {
method: "DELETE", method: "DELETE",
@@ -74,3 +76,33 @@ export const deleteEvent = (id:string) => {
console.error('Возникла ошибка с удалением:', error); console.error('Возникла ошибка с удалением:', error);
}); });
} }
//получение списка юзеров
export const UserList = (id:string) => {
return fetch(`${API_BASE}${API_EVENT}${id}/${API_USERS}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then(response => {
console.log(response.status);
if (response.ok) {
console.log('Получил:', response.headers.get('Location'));
return response.json();
} else {
throw new Error('Код ошибки: ' + response.status);
}
})
.then(data => {
console.log('Успешно:', data);
return data
})
.catch(error => {
console.error('Возникла ошибка с получением:', error);
});
}
@@ -51,6 +51,14 @@
justify-content:space-between; justify-content:space-between;
padding: 0; padding: 0;
} }
.model-content{
display: flex;
flex-direction: column;
justify-content:center;
}
@media (max-width: 820px) { @media (max-width: 820px) {
.general-left{ .general-left{
width: 100%; width: 100%;
+65 -38
View File
@@ -46,45 +46,72 @@ const Main = () => {
<div className={less["general-content"]}> <div className={less["general-content"]}>
<div className={less["general-left"]}> <div className={less["general-left"]}>
{events.map((event) => ( {events.map((event) => (
<Card className={`${less["card"]} flex flex-row `}> <Card className={`${less["card"]} flex flex-row `}>
<div className="flex flex-col"> <div className="flex flex-col">
<CardHeader className={less["header"]}> <CardHeader className={less["header"]}>
<div className={less["up"]}> <div className={less["up"]}>
<CardTitle className="p-0">{event.title}</CardTitle> <CardTitle className="p-0">{event.title}</CardTitle>
<CardDescription>Дата начала: {event.start_date}</CardDescription> <CardDescription>
</div> Start Date: {event.start_date}
{false && ( </CardDescription>
<Button size="icon" variant="ghost" onClick={() => deleteEvent(event.id)}><TrashIcon /></Button> </div>
)} {false && (
</CardHeader> <Button
<CardContent className="p-0 mt-4" > size="icon"
<p>{event.description}</p> variant="ghost"
</CardContent> onClick={() => deleteEvent(event.id)}
<Dialog> >
<DialogTrigger className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2">{t("respondRequest")}</DialogTrigger> <TrashIcon />
<DialogContent> </Button>
<DialogHeader> )}
<DialogTitle><h1 className={less["title-form"]}>{t("entrance")}</h1></DialogTitle> </CardHeader>
<DialogDescription> <CardContent className="p-0 mt-4">
<form className={less["input-form"]} onSubmit={(event) => submitRegister(event, navigate, graph)}> <p>{event.description}</p>
<div className={less["novis"]}><Input type="text" name="event" value={event.id} placeholder="Event" /></div> </CardContent>
<Input type="text" name="first_name" placeholder="First name" /> <Dialog>
<Input type="text" name="last_name" placeholder="Last name" /> <DialogTrigger className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2">
<Input type="date" name="birth_date" placeholder="Date" /> {t("respondRequest")}
<Input type="email" name="email" placeholder="Email" /> </DialogTrigger>
<Textarea name="bio" placeholder="About" /> <DialogContent className={less["model-content"]}>
<CheckboxTree data={event.tree} setGraph={setGraph} /> <DialogHeader>
<DialogTitle>
<Button>Signup</Button> <h1 className={less["title-form"]}>{t("entrance")}</h1>
</form> </DialogTitle>
</DialogDescription> </DialogHeader>
</DialogHeader> <form
</DialogContent> className={less["input-form"]}
</Dialog> onSubmit={(event) => submitRegister(event, navigate, graph)}
>
<div className={less["novis"]}>
<Input
type="text"
name="event"
value={event.id}
placeholder="Event"
/>
</div> </div>
</Card> <Input
))} type="text"
name="first_name"
placeholder="First name"
/>
<Input
type="text"
name="last_name"
placeholder="Last name"
/>
<Input type="date" name="birth_date" placeholder="Date" />
<Input type="email" name="email" placeholder="Email" />
<Textarea name="bio" placeholder="About" />
<CheckboxTree data={event.tree} setGraph={setGraph} />
<Button>Signup</Button>
</form>
</DialogContent>
</Dialog>
</div>
</Card>
))}
<Button variant="link" asChild> <Button variant="link" asChild>
<Link to={"/dash/admin"}>{t("iorganizer")}</Link> <Link to={"/dash/admin"}>{t("iorganizer")}</Link>
</Button> </Button>
@@ -1,10 +1,12 @@
.general{ .general{
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding-top: 100px; padding-top: 80px;
} }
.left{ .left{
width: 50%; width: 50%;
padding: 20px;
padding-top: 0;
} }
.right{ .right{
width: 50%; width: 50%;
@@ -12,4 +14,29 @@ width: 50%;
} }
.card{ .card{
margin: 10px; margin: 10px;
padding: 10px;
}
.input-form{
display: flex;
flex-direction: column;
gap: 8px;
}
.title{
padding: 0;
}
.up{
display: flex;
flex-direction: column;
}
.header{
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0;
}
.h2{
font-size: 24px;
font-weight: 600;
} }
@@ -9,7 +9,7 @@ import { Link } from "react-router-dom";
import { ToastAction } from "../../shared/ui/toast"; import { ToastAction } from "../../shared/ui/toast";
import { useToast } from "../../shared/ui/use-toast"; import { useToast } from "../../shared/ui/use-toast";
import { buttonVariants } from "../../ui/button"; import { buttonVariants } from "../../ui/button";
import { addEvent, submitRegister } from "../../widgets/Header/AuthAPI"; import { addEvent, deleteEvent, submitRegister } from "../../widgets/Header/AuthAPI";
import { import {
Card, Card,
CardContent, CardContent,
@@ -47,13 +47,14 @@ const SkillTree = () => {
return ( return (
<div className={less["general"]}> <div className={less["general"]}>
<div className={less["left"]}> <div className={less["left"]}>
<h2 className={less["h2"]}>Create event</h2>
<form className={less["input-form"]} onSubmit={(event) => addEvent(event)}> <form className={less["input-form"]} onSubmit={(event) => addEvent(event)}>
<Input type="text" name="title" placeholder="Event name" /> <Input type="text" name="title" placeholder="Event name" />
<Input type="text" name="description" placeholder="Last name" /> <Input type="text" name="description" placeholder="Last name" />
<Input type="date" name="start_date" placeholder="Start date" /> <Input type="date" name="start_date" placeholder="Start date" />
<Input type="date" name="end_date" placeholder="End date" /> <Input type="date" name="end_date" placeholder="End date" />
<Textarea name="description" placeholder="Description" /> <Textarea name="description" placeholder="Description" />
<Switch/> <Switch name="is_online"/>
<Button>{t("buttonLoginInSystem")}</Button> <Button>{t("buttonLoginInSystem")}</Button>
</form> </form>
</div> </div>
@@ -63,11 +64,11 @@ const SkillTree = () => {
<div className="flex flex-col"> <div className="flex flex-col">
<CardHeader className={less["header"]}> <CardHeader className={less["header"]}>
<div className={less["up"]}> <div className={less["up"]}>
<CardTitle className="p-0">{event.title}</CardTitle> <CardTitle className={less["title"]}>{event.title}</CardTitle>
<CardDescription>Дата начала: {event.start_date}</CardDescription> <CardDescription>Start date: {event.start_date}</CardDescription>
</div> </div>
{false && ( {true && (
<Button size="icon" variant="ghost" ><TrashIcon /></Button> <Button size="icon" variant="ghost"onClick={() =>{deleteEvent(event.id)}} ><TrashIcon /></Button>
)} )}
</CardHeader> </CardHeader>
<CardContent className="p-0 mt-4" > <CardContent className="p-0 mt-4" >
+3 -3
View File
@@ -19,7 +19,7 @@ i18n.use(initReactI18next).init({
LightTheme: "Светлая", LightTheme: "Светлая",
DarkTheme: "Темная", DarkTheme: "Темная",
SystemTheme: "Тема устройства", SystemTheme: "Тема устройства",
entrance: "Вход", entrance: "Подать заявку",
login: "Авторизация", login: "Авторизация",
registration: "Регистрация", registration: "Регистрация",
loginHeader: "Введите адрес электронной почты и пароль, чтобы начать.", loginHeader: "Введите адрес электронной почты и пароль, чтобы начать.",
@@ -58,7 +58,7 @@ i18n.use(initReactI18next).init({
LightTheme: "Light", LightTheme: "Light",
DarkTheme: "Dark", DarkTheme: "Dark",
SystemTheme: "System Theme", SystemTheme: "System Theme",
entrance: "Sign in", entrance: "Submit An Application",
login: "Log in", login: "Log in",
registration: "Sign up", registration: "Sign up",
loginHeader: " Enter your email address and password to get started.", loginHeader: " Enter your email address and password to get started.",
@@ -94,7 +94,7 @@ i18n.use(initReactI18next).init({
LightTheme: "光", LightTheme: "光",
DarkTheme: "黑暗", DarkTheme: "黑暗",
SystemTheme: "系统主题", SystemTheme: "系统主题",
entrance: "登入您的帐户", entrance: "递交申请",
login: "登录", login: "登录",
registration: "登记注册", registration: "登记注册",
loginHeader: "请输入您的电子邮件地址和密码,开始更改.", loginHeader: "请输入您的电子邮件地址和密码,开始更改.",