From fbedcf97e3f4c27d687f52af4b753be38881d3f8 Mon Sep 17 00:00:00 2001 From: Antoine Pelletier Date: Mon, 28 Sep 2026 16:35:30 +0200 Subject: [PATCH] wip --- .vscode/settings.json | 5 + LaTeX_pdf/assets/epfl.png | Bin 0 -> 12085 bytes LaTeX_pdf/assignment.cls | 60 ++++ LaTeX_pdf/box-filter.lua | 27 ++ LaTeX_pdf/modele.md | 78 +++++ LaTeX_pdf/template.tex | 156 ++++++++++ LaTeX_pdf/template.tex.bak | 138 +++++++++ frontend/src/components/AppContent.vue | 13 +- frontend/src/components/AppSidebar.vue | 2 +- frontend/src/components/ThemeToggle.vue | 25 ++ .../components/courses/CourseFormDialog.vue | 189 ++++++++++++ .../components/schedule/SlotFormDialog.vue | 204 +++++++++++++ frontend/src/components/ui/dialog/Dialog.vue | 4 +- .../src/components/ui/dialog/DialogClose.vue | 4 +- .../components/ui/dialog/DialogContent.vue | 19 +- .../ui/dialog/DialogDescription.vue | 14 +- .../src/components/ui/dialog/DialogFooter.vue | 15 +- .../src/components/ui/dialog/DialogHeader.vue | 10 +- .../ui/dialog/DialogScrollContent.vue | 33 +- .../src/components/ui/dialog/DialogTitle.vue | 21 +- .../components/ui/dialog/DialogTrigger.vue | 4 +- frontend/src/components/ui/dialog/index.ts | 18 +- frontend/src/components/ui/label/Label.vue | 14 +- frontend/src/components/ui/label/index.ts | 2 +- frontend/src/components/ui/select/Select.vue | 4 +- .../components/ui/select/SelectContent.vue | 47 +-- .../src/components/ui/select/SelectGroup.vue | 14 +- .../src/components/ui/select/SelectItem.vue | 21 +- .../components/ui/select/SelectItemText.vue | 4 +- .../src/components/ui/select/SelectLabel.vue | 10 +- .../ui/select/SelectScrollDownButton.vue | 21 +- .../ui/select/SelectScrollUpButton.vue | 21 +- .../components/ui/select/SelectSeparator.vue | 14 +- .../components/ui/select/SelectTrigger.vue | 26 +- .../src/components/ui/select/SelectValue.vue | 4 +- frontend/src/components/ui/select/index.ts | 22 +- .../components/ui/tags-input/TagsInput.vue | 24 +- .../ui/tags-input/TagsInputInput.vue | 19 +- .../ui/tags-input/TagsInputItem.vue | 24 +- .../ui/tags-input/TagsInputItemDelete.vue | 21 +- .../ui/tags-input/TagsInputItemText.vue | 19 +- .../src/components/ui/tags-input/index.ts | 10 +- frontend/src/lib/api.d.ts | 9 + frontend/src/locales/en.yml | 70 +++++ frontend/src/locales/fr.yml | 70 +++++ frontend/src/router/index.ts | 9 + frontend/src/services/api/client.ts | 3 + frontend/src/services/api/courses.ts | 104 +++++++ frontend/src/services/api/slot-tags.ts | 35 +++ frontend/src/styles/tailwind.css | 26 +- frontend/src/utils/timetable.ts | 85 ++++++ frontend/src/utils/types.ts | 17 +- frontend/src/views/CourseView.vue | 117 ++++++++ frontend/src/views/CoursesView.vue | 154 +++++++++- frontend/src/views/ScheduleView.vue | 282 +++++++++++++++++- src/api/courses.rs | 1 + 56 files changed, 2108 insertions(+), 254 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 LaTeX_pdf/assets/epfl.png create mode 100644 LaTeX_pdf/assignment.cls create mode 100644 LaTeX_pdf/box-filter.lua create mode 100644 LaTeX_pdf/modele.md create mode 100644 LaTeX_pdf/template.tex create mode 100644 LaTeX_pdf/template.tex.bak create mode 100644 frontend/src/components/ThemeToggle.vue create mode 100644 frontend/src/components/courses/CourseFormDialog.vue create mode 100644 frontend/src/components/schedule/SlotFormDialog.vue create mode 100644 frontend/src/services/api/courses.ts create mode 100644 frontend/src/services/api/slot-tags.ts create mode 100644 frontend/src/utils/timetable.ts create mode 100644 frontend/src/views/CourseView.vue diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bfc0469 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "i18n-ally.localesPaths": [ + "frontend/src/locales" + ] +} \ No newline at end of file diff --git a/LaTeX_pdf/assets/epfl.png b/LaTeX_pdf/assets/epfl.png new file mode 100644 index 0000000000000000000000000000000000000000..c54999e4eb099445153835bd89a672d3d874de69 GIT binary patch literal 12085 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 literal 0 HcmV?d00001 diff --git a/LaTeX_pdf/assignment.cls b/LaTeX_pdf/assignment.cls new file mode 100644 index 0000000..b327253 --- /dev/null +++ b/LaTeX_pdf/assignment.cls @@ -0,0 +1,60 @@ +% https://github.com/Ziqi-Yang/latex_assignment_template +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{assignment}[a latex class for editing your assignment] + +\LoadClass[11pt,letterpaper]{article} + +\RequirePackage[left=2cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} +\RequirePackage[dvipsnames]{xcolor} +\RequirePackage{listings} +\RequirePackage{tcolorbox} + +\setlength{\textwidth}{6.5in} +\setlength{\textheight}{9.in} +\setlength{\oddsidemargin}{0in} +\setlength{\headheight}{0in} + +% create a box to highlight word +\newtcbox{\hword}[1][orange]{on line, arc = 0.3mm,auto outer arc,colback = #1!12!, colframe = #1!60!,colupper = black, boxsep =0mm, left =0.3mm, right = 0.3mm, top =0.4mm, bottom = 0.4mm,boxrule =0.2mm, bottomrule =0.2mm, toprule = 0.2mm} + +% default code style +\lstset{ + framerule=1pt, + frame=tb, + emphstyle={\small\ttfamily\bfseries\color{Orange}}, + numbers=left, + numberstyle= \tiny\color{black}, + basicstyle = \small\ttfamily, + keywordstyle = \bfseries\color{BrickRed}, + identifierstyle = \bfseries\color{black}, + stringstyle = \bfseries\color{ForestGreen}, + commentstyle = \bfseries\color{Violet}, + breaklines = true, + columns = fixed, + basewidth = .5em, + backgroundcolor=\color{Gray!5}, + tabsize=2, + showspaces=false, + showstringspaces=false, +} + +\newcommand{\assignmentTitle}[5]{ + \begin{flushleft} + \textbf{Student Name:} #1\\ + \textbf{Student ID:} #2 + \end{flushleft} + + \begin{flushright}\vspace{-15mm} + \includegraphics[height=1cm]{#3} + \end{flushright} + + \begin{flushleft}\vspace{0mm} + \textbf{\large #4} + \end{flushleft} + + \begin{flushleft}\vspace{-3.5mm} + \textbf{\large #5} + \end{flushleft} + + \noindent\rule{\linewidth}{.6pt} +} diff --git a/LaTeX_pdf/box-filter.lua b/LaTeX_pdf/box-filter.lua new file mode 100644 index 0000000..7146bc1 --- /dev/null +++ b/LaTeX_pdf/box-filter.lua @@ -0,0 +1,27 @@ +-- box-filter.lua +-- Converts fenced divs like: +-- ::: {.defbox title="Def. 1.1 -- Something"} +-- content (normal Markdown: **bold**, lists, code blocks, math...) +-- ::: +-- into raw LaTeX tcolorbox environments (defbox/theobox/exbox), while the +-- content itself stays as ordinary Markdown blocks that pandoc converts +-- normally (bold -> \textbf, code fences -> lstlisting, etc.). + +local boxnames = { defbox = true, theobox = true, exbox = true } + +function Div(el) + for name, _ in pairs(boxnames) do + if el.classes:includes(name) then + local title = el.attributes["title"] or "" + local open = pandoc.RawBlock("latex", "\\begin{" .. name .. "}[" .. title .. "]") + local close = pandoc.RawBlock("latex", "\\end{" .. name .. "}") + local blocks = { open } + for _, b in ipairs(el.content) do + table.insert(blocks, b) + end + table.insert(blocks, close) + return blocks + end + end + return nil +end diff --git a/LaTeX_pdf/modele.md b/LaTeX_pdf/modele.md new file mode 100644 index 0000000..093ddb3 --- /dev/null +++ b/LaTeX_pdf/modele.md @@ -0,0 +1,78 @@ +--- +student-name: "Antoine Pelletier" +student-id: "356029" +logo: "/home/tibiscuit/LaTeX_pdf/assets/epfl.png" +course: "CS473 -- System Programming for SOC's" +subtitle: "Lecture 2 Notes: Memory subsystem" +--- + + + +# Introduction + +Texte d'introduction du cours. On peut utiliser du **gras**, de +l'*italique*, des listes : + +- premier point +- second point + - sous-point + +et des énumérations numérotées : + +1. étape une +2. étape deux + +## Sous-section + +On peut librement imbriquer des sous-sections avec `##`, `###`, etc. + +# Définitions, théorèmes et exemples + +Pour respecter le rendu exact du LaTeX d'origine, les encadrés colorés +sont insérés en LaTeX brut au milieu du Markdown : + +\begin{defbox}[Nom de la définition] +Contenu de la définition, en Markdown normal (gras, italique, etc. +fonctionnent aussi à l'intérieur). +\end{defbox} + +\begin{theobox}[Nom du théorème] +Énoncé du théorème ou de la propriété importante à retenir. +\end{theobox} + +\begin{exbox}[Nom de l'exemple] +Illustration concrète, éventuellement avec un extrait de code : + +```c +int main() { + return 0; +} +``` +\end{exbox} + +# Tableaux + +Un tableau Markdown standard : + +| Colonne A | Colonne B | Colonne C | +|:----------|:---------:|----------:| +| gauche | centré | droite | +| ligne 2 | ligne 2 | ligne 2 | + +# Conclusion + +Résumé des points clés du cours, comme dans la dernière slide de chaque +lecture. diff --git a/LaTeX_pdf/template.tex b/LaTeX_pdf/template.tex new file mode 100644 index 0000000..9317767 --- /dev/null +++ b/LaTeX_pdf/template.tex @@ -0,0 +1,156 @@ +\documentclass[11pt]{article} + +% ---------- Encoding & fonts (pdflatex-compatible, no external fonts needed) ---------- +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{lmodern} +\usepackage{textcomp} +% Unicode chars pdflatex doesn't know out of the box +\DeclareUnicodeCharacter{2212}{\ensuremath{-}} % − minus sign +\DeclareUnicodeCharacter{2192}{\ensuremath{\rightarrow}} +\DeclareUnicodeCharacter{2190}{\ensuremath{\leftarrow}} +\DeclareUnicodeCharacter{2264}{\ensuremath{\leq}} +\DeclareUnicodeCharacter{2265}{\ensuremath{\geq}} +\DeclareUnicodeCharacter{00D7}{\ensuremath{\times}} + +% ---------- Page geometry (matches assignment.cls) ---------- +\usepackage[left=2cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} +\usepackage{setspace} +\setstretch{1.08} + +% ---------- Math ---------- +\usepackage{amsmath,amssymb} + +% ---------- Colour & boxes ---------- +\usepackage[dvipsnames]{xcolor} +\usepackage[most]{tcolorbox} +\tcbuselibrary{skins,breakable} + +\newtcolorbox{defbox}[1][]{% + enhanced, breakable, colback=blue!5, colframe=blue!65!black, + fonttitle=\bfseries, coltitle=white, title={#1}, + boxrule=0.8pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt +} +\newtcolorbox{theobox}[1][]{% + enhanced, breakable, colback=red!5, colframe=red!55!black, + fonttitle=\bfseries, coltitle=white, title={#1}, + boxrule=0.8pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt +} +\newtcolorbox{exbox}[1][]{% + enhanced, breakable, colback=green!5, colframe=green!40!black, + fonttitle=\bfseries, coltitle=white, title={#1}, + boxrule=0.8pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt +} + +% Word-highlight box, matching assignment.cls's \hword +\newtcbox{\hword}[1][orange]{on line, arc=0.3mm, auto outer arc, + colback=#1!12!, colframe=#1!60!, colupper=black, boxsep=0mm, + left=0.3mm, right=0.3mm, top=0.4mm, bottom=0.4mm, + boxrule=0.2mm, bottomrule=0.2mm, toprule=0.2mm} + +% ---------- Tables ---------- +\usepackage{calc} +\usepackage{booktabs} +\usepackage{longtable} +\usepackage{array} +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +\providecommand{\passthrough}[1]{#1} +\providecommand{\pandocbounded}[1]{#1} + +% Fix for a real bug (see jgm/pandoc#11201): longtable >= 4.21 (shipped with +% recent TeX Live, e.g. 2026) combined with the caption package's longtable +% patch tries to reference a counter literally named "none" as a placeholder +% for uncaptioned tables, which was never actually defined anywhere, crashing +% with "LaTeX Error: No counter 'none' defined." Defining it as an unused +% dummy counter satisfies that reference harmlessly. +\newcounter{none} + +% ---------- Code listings (pandoc --listings), matching assignment.cls ---------- +\usepackage{listings} +\usepackage{pmboxdraw} % box-drawing glyphs (├ └ │ ─ ...) under pdflatex +\lstset{ + % listings is not UTF-8 aware under pdflatex: map multibyte chars explicitly + literate= + {├}{{\textSFviii}}1 {└}{{\textSFii}}1 {┌}{{\textSFi}}1 {┐}{{\textSFiii}}1 {┘}{{\textSFiv}}1 {┤}{{\textSFix}}1 + {┬}{{\textSFvi}}1 {┴}{{\textSFvii}}1 {┼}{{\textSFv}}1 {│}{{\textSFxi}}1 {─}{{\textSFx}}1 + {é}{{\'e}}1 {è}{{\`e}}1 {ê}{{\^e}}1 {ë}{{\"e}}1 {à}{{\`a}}1 {â}{{\^a}}1 + {ù}{{\`u}}1 {û}{{\^u}}1 {ü}{{\"u}}1 {ô}{{\^o}}1 {ö}{{\"o}}1 {î}{{\^i}}1 + {ï}{{\"i}}1 {ç}{{\c{c}}}1 {É}{{\'E}}1 {È}{{\`E}}1 {À}{{\`A}}1 {Ç}{{\c{C}}}1 + {«}{{\guillemotleft}}1 {»}{{\guillemotright}}1 {’}{{'}}1 {…}{{...}}3 {−}{{-}}1 {–}{{--}}1 {—}{{---}}1 + {≤}{{$$\leq$$}}1 {≥}{{$$\geq$$}}1 {×}{{$$\times$$}}1 + {→}{{$$\rightarrow$$}}2 {←}{{$$\leftarrow$$}}2, + framerule=1pt, + frame=tb, + emphstyle={\small\ttfamily\bfseries\color{Orange}}, + numbers=left, + numberstyle=\tiny\color{black}, + basicstyle=\small\ttfamily, + keywordstyle=\bfseries\color{BrickRed}, + identifierstyle=\bfseries\color{black}, + stringstyle=\bfseries\color{ForestGreen}, + commentstyle=\bfseries\color{Violet}, + breaklines=true, + columns=fixed, + basewidth=.5em, + backgroundcolor=\color{Gray!5}, + tabsize=2, + showspaces=false, + showstringspaces=false, +} + +% ---------- Links ---------- +\usepackage[colorlinks=true,linkcolor=blue!60!black,urlcolor=blue!60!black,citecolor=blue!60!black]{hyperref} + +% caption is loaded AFTER hyperref on purpose: it needs to detect hyperref's +% presence at load time to patch longtable's anchor/bookmark generation +% correctly. Loading it before hyperref (as an earlier version of this +% template did) is a known cause of exactly the +% "LaTeX Error: No counter 'none' defined" crash on \begin{longtable}. +\usepackage{caption} +\captionsetup{skip=6pt} + +% Failsafe belt-and-braces: bypass hyperref's auto-computed \theHtable / +% \theHfigure chain entirely. +\providecommand{\theHtable}{\thetable} +\providecommand{\theHfigure}{\thefigure} + +% ---------- Section styling ---------- +\usepackage{sectsty} +\sectionfont{\Large\bfseries} +\subsectionfont{\large\bfseries\color{black!80}} + +\usepackage{graphicx} + +\begin{document} + +% ---------- Header (matches assignment.cls's \assignmentTitle) ---------- +\begin{flushleft} +$if(student-name)$\textbf{Student Name:} $student-name$\\$endif$ +$if(student-id)$\textbf{Student ID:} $student-id$$endif$ +\end{flushleft} + +$if(logo)$ +\begin{flushright}\vspace{-15mm} +\includegraphics[height=1cm]{$logo$} +\end{flushright} +$endif$ + +$if(course)$ +\begin{flushleft}\vspace{0mm} +\textbf{\large $course$} +\end{flushleft} +$endif$ + +$if(subtitle)$ +\begin{flushleft}\vspace{-3.5mm} +\textbf{\large $subtitle$} +\end{flushleft} +$endif$ + +\noindent\rule{\linewidth}{.6pt} +\vspace{4mm} + +$body$ + +\end{document} diff --git a/LaTeX_pdf/template.tex.bak b/LaTeX_pdf/template.tex.bak new file mode 100644 index 0000000..5d7f2ce --- /dev/null +++ b/LaTeX_pdf/template.tex.bak @@ -0,0 +1,138 @@ +\documentclass[11pt]{article} + +% ---------- Encoding & fonts (pdflatex-compatible, no external fonts needed) ---------- +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{lmodern} +\usepackage{textcomp} + +% ---------- Page geometry (matches assignment.cls) ---------- +\usepackage[left=2cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} +\usepackage{setspace} +\setstretch{1.08} + +% ---------- Math ---------- +\usepackage{amsmath,amssymb} + +% ---------- Colour & boxes ---------- +\usepackage[dvipsnames]{xcolor} +\usepackage[most]{tcolorbox} +\tcbuselibrary{skins,breakable} + +\newtcolorbox{defbox}[1][]{% + enhanced, breakable, colback=blue!5, colframe=blue!65!black, + fonttitle=\bfseries, coltitle=white, title={#1}, + boxrule=0.8pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt +} +\newtcolorbox{theobox}[1][]{% + enhanced, breakable, colback=red!5, colframe=red!55!black, + fonttitle=\bfseries, coltitle=white, title={#1}, + boxrule=0.8pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt +} +\newtcolorbox{exbox}[1][]{% + enhanced, breakable, colback=green!5, colframe=green!40!black, + fonttitle=\bfseries, coltitle=white, title={#1}, + boxrule=0.8pt, arc=2pt, left=6pt, right=6pt, top=4pt, bottom=4pt +} + +% Word-highlight box, matching assignment.cls's \hword +\newtcbox{\hword}[1][orange]{on line, arc=0.3mm, auto outer arc, + colback=#1!12!, colframe=#1!60!, colupper=black, boxsep=0mm, + left=0.3mm, right=0.3mm, top=0.4mm, bottom=0.4mm, + boxrule=0.2mm, bottomrule=0.2mm, toprule=0.2mm} + +% ---------- Tables ---------- +\usepackage{calc} +\usepackage{booktabs} +\usepackage{longtable} +\usepackage{array} +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +\providecommand{\passthrough}[1]{#1} +\providecommand{\pandocbounded}[1]{#1} + +% Fix for a real bug (see jgm/pandoc#11201): longtable >= 4.21 (shipped with +% recent TeX Live, e.g. 2026) combined with the caption package's longtable +% patch tries to reference a counter literally named "none" as a placeholder +% for uncaptioned tables, which was never actually defined anywhere, crashing +% with "LaTeX Error: No counter 'none' defined." Defining it as an unused +% dummy counter satisfies that reference harmlessly. +\newcounter{none} + +% ---------- Code listings (pandoc --listings), matching assignment.cls ---------- +\usepackage{listings} +\lstset{ + framerule=1pt, + frame=tb, + emphstyle={\small\ttfamily\bfseries\color{Orange}}, + numbers=left, + numberstyle=\tiny\color{black}, + basicstyle=\small\ttfamily, + keywordstyle=\bfseries\color{BrickRed}, + identifierstyle=\bfseries\color{black}, + stringstyle=\bfseries\color{ForestGreen}, + commentstyle=\bfseries\color{Violet}, + breaklines=true, + columns=fixed, + basewidth=.5em, + backgroundcolor=\color{Gray!5}, + tabsize=2, + showspaces=false, + showstringspaces=false, +} + +% ---------- Links ---------- +\usepackage[colorlinks=true,linkcolor=blue!60!black,urlcolor=blue!60!black,citecolor=blue!60!black]{hyperref} + +% caption is loaded AFTER hyperref on purpose: it needs to detect hyperref's +% presence at load time to patch longtable's anchor/bookmark generation +% correctly. Loading it before hyperref (as an earlier version of this +% template did) is a known cause of exactly the +% "LaTeX Error: No counter 'none' defined" crash on \begin{longtable}. +\usepackage{caption} +\captionsetup{skip=6pt} + +% Failsafe belt-and-braces: bypass hyperref's auto-computed \theHtable / +% \theHfigure chain entirely. +\providecommand{\theHtable}{\thetable} +\providecommand{\theHfigure}{\thefigure} + +% ---------- Section styling ---------- +\usepackage{sectsty} +\sectionfont{\Large\bfseries} +\subsectionfont{\large\bfseries\color{black!80}} + +\usepackage{graphicx} + +\begin{document} + +% ---------- Header (matches assignment.cls's \assignmentTitle) ---------- +\begin{flushleft} +$if(student-name)$\textbf{Student Name:} $student-name$\\$endif$ +$if(student-id)$\textbf{Student ID:} $student-id$$endif$ +\end{flushleft} + +$if(logo)$ +\begin{flushright}\vspace{-15mm} +\includegraphics[height=1cm]{$logo$} +\end{flushright} +$endif$ + +$if(course)$ +\begin{flushleft}\vspace{0mm} +\textbf{\large $course$} +\end{flushleft} +$endif$ + +$if(subtitle)$ +\begin{flushleft}\vspace{-3.5mm} +\textbf{\large $subtitle$} +\end{flushleft} +$endif$ + +\noindent\rule{\linewidth}{.6pt} +\vspace{4mm} + +$body$ + +\end{document} diff --git a/frontend/src/components/AppContent.vue b/frontend/src/components/AppContent.vue index da44fd4..926e0c1 100644 --- a/frontend/src/components/AppContent.vue +++ b/frontend/src/components/AppContent.vue @@ -11,14 +11,15 @@ import { } from '@/components/ui/dropdown-menu' import { Separator } from '@/components/ui/separator' import { SidebarInset, SidebarTrigger } from '@/components/ui/sidebar' +import ThemeToggle from '@/components/ThemeToggle.vue' import { locales, setLocale } from '@/services/i18n' import { useI18n } from 'vue-i18n' const route = useRoute() const { locale: currentLocale } = useI18n() -const selectedLangFlag = computed( - () => locales.find((l) => l.lang === currentLocale.value)?.flag ?? locales[0].flag, +const selectedLocale = computed( + () => locales.find((l) => l.lang === currentLocale.value) ?? locales[0], ) @@ -29,20 +30,22 @@ const selectedLangFlag = computed(

{{ route.meta.title ? $t(route.meta.title) : '' }}

+ - + {{ locale.lang.toUpperCase() }} diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index 9358251..c766af3 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -66,7 +66,7 @@ function onNavigate() { diff --git a/frontend/src/components/ThemeToggle.vue b/frontend/src/components/ThemeToggle.vue new file mode 100644 index 0000000..1842ae4 --- /dev/null +++ b/frontend/src/components/ThemeToggle.vue @@ -0,0 +1,25 @@ + + + diff --git a/frontend/src/components/courses/CourseFormDialog.vue b/frontend/src/components/courses/CourseFormDialog.vue new file mode 100644 index 0000000..7dac4dd --- /dev/null +++ b/frontend/src/components/courses/CourseFormDialog.vue @@ -0,0 +1,189 @@ + + + diff --git a/frontend/src/components/schedule/SlotFormDialog.vue b/frontend/src/components/schedule/SlotFormDialog.vue new file mode 100644 index 0000000..7a2b367 --- /dev/null +++ b/frontend/src/components/schedule/SlotFormDialog.vue @@ -0,0 +1,204 @@ + + + diff --git a/frontend/src/components/ui/dialog/Dialog.vue b/frontend/src/components/ui/dialog/Dialog.vue index 47b0968..ff50359 100644 --- a/frontend/src/components/ui/dialog/Dialog.vue +++ b/frontend/src/components/ui/dialog/Dialog.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/components/ui/dialog/DialogContent.vue b/frontend/src/components/ui/dialog/DialogContent.vue index 4bc0a1c..b2bf24a 100644 --- a/frontend/src/components/ui/dialog/DialogContent.vue +++ b/frontend/src/components/ui/dialog/DialogContent.vue @@ -1,21 +1,21 @@ @@ -31,7 +31,8 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits) cn( 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg', props.class, - )" + ) + " > diff --git a/frontend/src/components/ui/dialog/DialogDescription.vue b/frontend/src/components/ui/dialog/DialogDescription.vue index 062c3a5..bf04fdc 100644 --- a/frontend/src/components/ui/dialog/DialogDescription.vue +++ b/frontend/src/components/ui/dialog/DialogDescription.vue @@ -1,13 +1,13 @@ diff --git a/frontend/src/components/ui/dialog/DialogFooter.vue b/frontend/src/components/ui/dialog/DialogFooter.vue index 5f481e5..c6f5d4e 100644 --- a/frontend/src/components/ui/dialog/DialogFooter.vue +++ b/frontend/src/components/ui/dialog/DialogFooter.vue @@ -1,19 +1,12 @@ diff --git a/frontend/src/components/ui/dialog/DialogHeader.vue b/frontend/src/components/ui/dialog/DialogHeader.vue index 33aa003..e22a1fd 100644 --- a/frontend/src/components/ui/dialog/DialogHeader.vue +++ b/frontend/src/components/ui/dialog/DialogHeader.vue @@ -1,16 +1,14 @@ diff --git a/frontend/src/components/ui/dialog/DialogScrollContent.vue b/frontend/src/components/ui/dialog/DialogScrollContent.vue index 59a32ec..46bb80e 100644 --- a/frontend/src/components/ui/dialog/DialogScrollContent.vue +++ b/frontend/src/components/ui/dialog/DialogScrollContent.vue @@ -1,21 +1,21 @@ @@ -33,13 +33,18 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits) ) " v-bind="forwarded" - @pointer-down-outside="(event) => { - const originalEvent = event.detail.originalEvent; - const target = originalEvent.target as HTMLElement; - if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) { - event.preventDefault(); + @pointer-down-outside=" + (event) => { + const originalEvent = event.detail.originalEvent + const target = originalEvent.target as HTMLElement + if ( + originalEvent.offsetX > target.clientWidth || + originalEvent.offsetY > target.clientHeight + ) { + event.preventDefault() + } } - }" + " > diff --git a/frontend/src/components/ui/dialog/DialogTitle.vue b/frontend/src/components/ui/dialog/DialogTitle.vue index 1de56de..b4dfbeb 100644 --- a/frontend/src/components/ui/dialog/DialogTitle.vue +++ b/frontend/src/components/ui/dialog/DialogTitle.vue @@ -1,13 +1,13 @@ @@ -15,12 +15,7 @@ const forwardedProps = useForwardProps(delegatedProps)