├── frontend
├── .husky
│ └── pre-commit
├── src
│ ├── component
│ │ ├── Sidebar
│ │ │ └── index.tsx
│ │ ├── Clock
│ │ │ ├── index.tsx
│ │ │ └── Clock.tsx
│ │ ├── DropBox
│ │ │ ├── index.ts
│ │ │ └── DropBox.tsx
│ │ ├── Main
│ │ │ ├── index.tsx
│ │ │ └── Main.tsx
│ │ ├── Layout
│ │ │ ├── index.tsx
│ │ │ └── Layout.tsx
│ │ ├── Router
│ │ │ └── index.tsx
│ │ ├── TodoCard
│ │ │ └── index.tsx
│ │ ├── UserBox
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── DialogRemoveAvatar.tsx
│ │ ├── Column
│ │ │ └── index.tsx
│ │ ├── SubDrawer
│ │ │ ├── index.tsx
│ │ │ └── SubDrawer.tsx
│ │ ├── UserAvatar
│ │ │ ├── index.tsx
│ │ │ └── UserAvatar.tsx
│ │ ├── GroupUserCard
│ │ │ ├── index.tsx
│ │ │ └── RemoveGroupDialog.tsx
│ │ ├── PasswordField
│ │ │ ├── index.tsx
│ │ │ └── PasswordField.tsx
│ │ ├── ProjectAvatar
│ │ │ ├── index.tsx
│ │ │ └── ProjectAvatar.tsx
│ │ ├── ProjectCard
│ │ │ └── index.tsx
│ │ ├── ProjectMenu
│ │ │ ├── index.tsx
│ │ │ └── ProjectMenu.tsx
│ │ ├── SearchField
│ │ │ ├── index.tsx
│ │ │ └── SearchField.tsx
│ │ ├── NotificationBox
│ │ │ └── index.tsx
│ │ ├── ProjectTaskCard
│ │ │ └── index.tsx
│ │ ├── GroupProjectCard
│ │ │ └── index.tsx
│ │ ├── GroupProjectMenu
│ │ │ ├── index.tsx
│ │ │ └── GroupProjectMenu.tsx
│ │ ├── ProjectDeleteDialog
│ │ │ └── index.tsx
│ │ ├── GroupProjectTaskCard
│ │ │ └── index.tsx
│ │ └── InfiniteScroll
│ │ │ └── InfiniteScroll.tsx
│ ├── Theme
│ │ ├── index.tsx
│ │ ├── Animations.ts
│ │ └── Color.ts
│ ├── View
│ │ ├── HomeView
│ │ │ ├── index.tsx
│ │ │ └── HomeView.tsx
│ │ ├── GroupView
│ │ │ ├── index.tsx
│ │ │ ├── GroupProjectTaskView
│ │ │ │ ├── index.tsx
│ │ │ │ └── schema.ts
│ │ │ ├── GroupProjectView
│ │ │ │ ├── UpdateGroup
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── CreateForm.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ ├── ProjectTab.tsx
│ │ │ │ └── UsersTab.tsx
│ │ │ └── GroupView.tsx
│ │ ├── LoginView
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── LoginForm.tsx
│ │ ├── LogoutView
│ │ │ ├── index.tsx
│ │ │ └── LogoutView.tsx
│ │ ├── NotesView
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ ├── NoNoteView.tsx
│ │ │ ├── DrawerLink.tsx
│ │ │ ├── NotesView.tsx
│ │ │ └── DialogDelete.tsx
│ │ ├── CalendarView
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ ├── MonthView
│ │ │ │ └── EventChip.tsx
│ │ │ ├── WeekView
│ │ │ │ └── WeekEventChip.tsx
│ │ │ └── DialogCreate.tsx
│ │ ├── DashboardView
│ │ │ ├── index.tsx
│ │ │ ├── Sections
│ │ │ │ └── TodoSection.tsx
│ │ │ └── DashboardView.tsx
│ │ ├── ProjectTask
│ │ │ ├── index.tsx
│ │ │ └── schema.ts
│ │ ├── RegisterView
│ │ │ ├── index.tsx
│ │ │ ├── PasswordRenew
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── EmailSection.tsx
│ │ │ ├── schema.ts
│ │ │ └── RegisterView.tsx
│ │ ├── SettingsView
│ │ │ ├── index.tsx
│ │ │ └── Section
│ │ │ │ ├── index.tsx
│ │ │ │ ├── LanguageSection.tsx
│ │ │ │ ├── RouteSection.tsx
│ │ │ │ ├── ColorSection.tsx
│ │ │ │ ├── ReminderSection.tsx
│ │ │ │ └── ThemeSection.tsx
│ │ ├── NoConnectionView
│ │ │ ├── index.tsx
│ │ │ └── NoConnectionView.tsx
│ │ ├── Errors
│ │ │ ├── index.tsx
│ │ │ ├── NotFoundView.tsx
│ │ │ └── ErrorView.tsx
│ │ ├── TodoView
│ │ │ ├── TodoTask
│ │ │ │ ├── schema.ts
│ │ │ │ └── CreateForm.tsx
│ │ │ ├── schema.ts
│ │ │ ├── NoTodoView.tsx
│ │ │ ├── TodoPreview.tsx
│ │ │ ├── TodoView.tsx
│ │ │ ├── DialogDelete.tsx
│ │ │ └── TodoLink.tsx
│ │ ├── LoadingView
│ │ │ └── LoadingView.tsx
│ │ └── ProjectView
│ │ │ ├── schema.ts
│ │ │ └── ProjectView.tsx
│ ├── vite-env.d.ts
│ ├── assets
│ │ └── Logo.png
│ ├── config
│ │ └── constants.ts
│ ├── api
│ │ ├── pagination.ts
│ │ ├── Image
│ │ │ ├── api.ts
│ │ │ └── query.ts
│ │ ├── Settings
│ │ │ ├── api.ts
│ │ │ └── query.ts
│ │ ├── Todos
│ │ │ ├── api.ts
│ │ │ └── query.ts
│ │ ├── Notifications
│ │ │ ├── query.ts
│ │ │ └── api.ts
│ │ ├── Dashboard
│ │ │ └── query.ts
│ │ ├── Calendar
│ │ │ ├── api.ts
│ │ │ └── query.ts
│ │ ├── Notes
│ │ │ └── api.ts
│ │ ├── User
│ │ │ └── api.ts
│ │ └── ProjectTodos
│ │ │ └── api.ts
│ ├── utils
│ │ ├── dateTime.ts
│ │ ├── debounce.ts
│ │ ├── deriveBarColor.ts
│ │ ├── deriveProjectStatus.ts
│ │ ├── useGroupRoles.ts
│ │ ├── useCurrentDate.ts
│ │ ├── useDebouncedValue.ts
│ │ ├── useCurrentWeek.ts
│ │ └── userAvatar.ts
│ ├── main.tsx
│ └── context
│ │ ├── PdfContext.tsx
│ │ ├── GroupRole.tsx
│ │ ├── CalendarContext.tsx
│ │ └── AuthContext.tsx
├── src-tauri
│ ├── build.rs
│ ├── icons
│ │ ├── 32x32.png
│ │ ├── icon.icns
│ │ ├── icon.ico
│ │ ├── icon.png
│ │ ├── 128x128.png
│ │ ├── zenith.png
│ │ ├── 128x128@2x.png
│ │ ├── StoreLogo.png
│ │ ├── Square30x30Logo.png
│ │ ├── Square44x44Logo.png
│ │ ├── Square71x71Logo.png
│ │ ├── Square89x89Logo.png
│ │ ├── Square107x107Logo.png
│ │ ├── Square142x142Logo.png
│ │ ├── Square150x150Logo.png
│ │ ├── Square284x284Logo.png
│ │ └── Square310x310Logo.png
│ ├── .gitignore
│ ├── src
│ │ └── main.rs
│ ├── Cargo.toml
│ └── tauri.conf.json
├── icon.ico
├── .env.example
├── .vscode
│ └── extensions.json
├── .prettierrc
├── .eslint.config.js
├── tsconfig.node.json
├── lingui.config.ts
├── .gitignore
├── README.md
├── index.html
├── tsconfig.json
├── .eslintrc.json
├── vite.config.ts
└── public
│ └── vite.svg
├── assets
├── Zenith.png
├── notes.png
├── todo.png
├── calendar.png
├── projects.png
├── register.png
├── settings.png
└── projecttask.png
└── backend
├── backend
├── backend.http
├── Dto
│ ├── Groups
│ │ ├── LeaveGroupDto.cs
│ │ ├── ChangeRoleDto.cs
│ │ ├── GroupEditDto.cs
│ │ ├── GroupByIdDto.cs
│ │ └── GroupUsersDto.cs
│ ├── Todos
│ │ ├── ToggleTodoDto.cs
│ │ ├── AddTodoDto.cs
│ │ └── TodoDto.cs
│ ├── Users
│ │ ├── TokenDto.cs
│ │ ├── ForgotPasswordDto.cs
│ │ ├── LoginUserDto.cs
│ │ ├── ResetPasswordDto.cs
│ │ ├── UserDto.cs
│ │ ├── UpdateUserDto.cs
│ │ └── RegisterUserDto.cs
│ ├── Images
│ │ └── AddImageDto.cs
│ ├── Notes
│ │ ├── EditNoteDto.cs
│ │ └── AllNotesDto.cs
│ ├── CalendarEvents
│ │ ├── EventPaginationDto.cs
│ │ ├── CalendarEventDto.cs
│ │ └── AllCalendarEventsDto.cs
│ ├── ProjectTasks
│ │ ├── ProjectTaskStatusDto.cs
│ │ ├── ProjectTaskDto.cs
│ │ ├── AddProjectTaskDto.cs
│ │ └── ProjectTaskShortDto.cs
│ ├── Token
│ │ └── AccessTokenDto.cs
│ ├── Dashboard
│ │ ├── DashboardNoteDto.cs
│ │ ├── DashboardTodoDto.cs
│ │ ├── DashboardGroupProjectDto.cs
│ │ └── DashboardProjectDto.cs
│ ├── Pagination
│ │ ├── PaginationRequestDto.cs
│ │ └── PaginationResponseDto.cs
│ ├── ProjectTodo
│ │ ├── AddProjectTodoDto.cs
│ │ ├── ProjectTodoDto.cs
│ │ └── AllProjectsTodoDto.cs
│ ├── Projects
│ │ ├── AddProjectDto.cs
│ │ ├── EditProjectDto.cs
│ │ ├── AllProjectsDto.cs
│ │ ├── ProjectDto.cs
│ │ └── ProjectByStatusDto.cs
│ ├── GroupProjects
│ │ ├── AddGroupProjectDto.cs
│ │ ├── AllGroupProjectsDto.cs
│ │ └── GroupProjectByStatusDto.cs
│ ├── GroupProjectTasks
│ │ ├── AddGroupProjectTaskDto.cs
│ │ ├── GroupProjectTaskDto.cs
│ │ └── GroupProjectTaskShortDto.cs
│ ├── UserPreferences
│ │ └── UserPreferencesDto.cs
│ ├── Validators
│ │ └── RegisterUserDtoValidator.cs
│ └── Notifications
│ │ └── NotificationDto.cs
├── Enums
│ ├── Roles.cs
│ ├── GroupRole.cs
│ ├── ProjectStatus.cs
│ └── ProjectTaskStatus.cs
├── appsettings.Development.json
├── EmailSettings.cs
├── Interface
│ ├── IEmailRepository.cs
│ ├── IUserContextRepository.cs
│ ├── INotificationRepository.cs
│ ├── IUserPreferencesRepository.cs
│ ├── IKanbanTaskRepository.cs
│ ├── ICalendarEventRepository.cs
│ ├── ITodoRepository.cs
│ ├── IProjectRepository.cs
│ ├── IProjectTaskRepository.cs
│ ├── IProjectTodoRepository.cs
│ ├── IDashboardRepository.cs
│ ├── IGroupProjectTaskRepository.cs
│ ├── IGroupProjectRepository.cs
│ ├── INoteRepository.cs
│ ├── IUserRepository.cs
│ └── IGroupRepository.cs
├── Exceptions
│ └── NotFoundException.cs
├── appsettings.json
├── AuthSettings.cs
├── Models
│ ├── GroupRole.cs
│ ├── Todo.cs
│ ├── Group.cs
│ ├── Note.cs
│ ├── ProjectTodo.cs
│ ├── KanbanTask.cs
│ ├── ProjectTask.cs
│ ├── UserPreferences.cs
│ ├── CalendarEvent.cs
│ ├── GroupProjectTask.cs
│ ├── Project.cs
│ ├── GroupProject.cs
│ ├── Notification.cs
│ └── User.cs
├── Store
│ └── StringGenerator.cs
├── .env.example
├── Migrations
│ ├── 20240616124753_avatar_images.cs
│ ├── 20240708220343_refresh-token.cs
│ └── 20241106171010_calendar_colors.cs
├── Controllers
│ ├── TokenController.cs
│ ├── SettingsController.cs
│ ├── NotificationController.cs
│ ├── ImageController.cs
│ ├── TodoController.cs
│ ├── CalendarEventController.cs
│ ├── ProjectController.cs
│ └── ProjectTodoController.cs
├── Repository
│ ├── UserContextRepository.cs
│ └── EmailRepository.cs
├── Properties
│ └── launchSettings.json
├── MapProfile.cs
└── backend.csproj
├── docker-compose.yaml
├── backend.sln
└── dockerfile
/frontend/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | pnpm test
2 |
--------------------------------------------------------------------------------
/frontend/src/component/Sidebar/index.tsx:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/Theme/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Theme";
2 |
--------------------------------------------------------------------------------
/frontend/src/View/HomeView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './HomeView';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/Clock/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './Clock';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/DropBox/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DropBox'
--------------------------------------------------------------------------------
/frontend/src/component/Main/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Main";
2 |
--------------------------------------------------------------------------------
/frontend/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './GroupView';
2 |
--------------------------------------------------------------------------------
/frontend/src/View/LoginView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./LoginView";
2 |
--------------------------------------------------------------------------------
/frontend/src/View/LogoutView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './LogoutView';
2 |
--------------------------------------------------------------------------------
/frontend/src/View/NotesView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./NotesView";
2 |
--------------------------------------------------------------------------------
/frontend/src/component/Layout/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './Layout';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/Router/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Router";
2 |
--------------------------------------------------------------------------------
/frontend/src/component/TodoCard/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './TodoCard';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/UserBox/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './UserBox';
2 |
--------------------------------------------------------------------------------
/frontend/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/src/View/CalendarView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './CalendarView';
2 |
--------------------------------------------------------------------------------
/frontend/src/View/DashboardView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './DashboardView';
2 |
--------------------------------------------------------------------------------
/frontend/src/View/ProjectTask/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ProjectTaskView";
2 |
--------------------------------------------------------------------------------
/frontend/src/View/RegisterView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./RegisterView";
2 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './SettingsView';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/Column/index.tsx:
--------------------------------------------------------------------------------
1 | export { Column } from './Column';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/SubDrawer/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./SubDrawer";
2 |
--------------------------------------------------------------------------------
/frontend/src/component/UserAvatar/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './UserAvatar';
2 |
--------------------------------------------------------------------------------
/assets/Zenith.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/Zenith.png
--------------------------------------------------------------------------------
/assets/notes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/notes.png
--------------------------------------------------------------------------------
/assets/todo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/todo.png
--------------------------------------------------------------------------------
/frontend/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/icon.ico
--------------------------------------------------------------------------------
/frontend/src/component/GroupUserCard/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './GroupUserCard';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/PasswordField/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './PasswordField';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectAvatar/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './ProjectAvatar';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectCard/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ProjectCard";
2 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectMenu/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ProjectMenu";
2 |
--------------------------------------------------------------------------------
/frontend/src/component/SearchField/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './SearchField';
2 |
--------------------------------------------------------------------------------
/assets/calendar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/calendar.png
--------------------------------------------------------------------------------
/assets/projects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/projects.png
--------------------------------------------------------------------------------
/assets/register.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/register.png
--------------------------------------------------------------------------------
/assets/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/settings.png
--------------------------------------------------------------------------------
/frontend/src/View/NoConnectionView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './NoConnectionView';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/NotificationBox/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './NotificationBox';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectTaskCard/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ProjectTaskCard";
2 |
--------------------------------------------------------------------------------
/assets/projecttask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/assets/projecttask.png
--------------------------------------------------------------------------------
/frontend/src/component/GroupProjectCard/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './GroupProjectCard';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/GroupProjectMenu/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './GroupProjectMenu';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectDeleteDialog/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './ProjectDelete';
2 |
--------------------------------------------------------------------------------
/frontend/src/component/GroupProjectTaskCard/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './GroupProjectTaskCard';
2 |
--------------------------------------------------------------------------------
/frontend/src/View/Errors/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ErrorView";
2 | export * from "./NotFoundView";
3 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectTaskView/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './GroupProjectTaskView';
2 |
--------------------------------------------------------------------------------
/frontend/src/assets/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src/assets/Logo.png
--------------------------------------------------------------------------------
/frontend/.env.example:
--------------------------------------------------------------------------------
1 | #VITE_API_URL="https://localhost:7086"
2 | # DOCKER
3 | VITE_API_URL="http://localhost:5000"
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/frontend/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 |
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/zenith.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/zenith.png
--------------------------------------------------------------------------------
/frontend/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/frontend/src/View/RegisterView/PasswordRenew/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './DialogPassword';
2 | export * from './PasswordForm';
3 |
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/frontend/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dewy01/Zenith/HEAD/frontend/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/backend/backend/backend.http:
--------------------------------------------------------------------------------
1 | @backend_HostAddress = http://localhost:5246
2 |
3 | GET {{backend_HostAddress}}/weatherforecast/
4 | Accept: application/json
5 |
6 | ###
7 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Groups/LeaveGroupDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Groups
2 | {
3 | public class LeaveGroupDto
4 | {
5 | public int GroupID { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Todos/ToggleTodoDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Todos
2 | {
3 | public class ToggleTodoDto
4 | {
5 | public bool isDone { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/TokenDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class TokenDto
4 | {
5 | public required string Token { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/Enums/Roles.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Enums
2 | {
3 | public enum Roles
4 | {
5 | Unverified,
6 | User,
7 | Admin
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "useTabs": false,
4 | "semi": true,
5 | "singleQuote": true,
6 | "trailingComma": "all",
7 | "printWidth": 80
8 | }
--------------------------------------------------------------------------------
/backend/backend/Dto/Groups/ChangeRoleDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Groups
2 | {
3 | public class ChangeRoleDto
4 | {
5 | public int UserId { get; set; }
6 | }
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Images/AddImageDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Images
2 | {
3 | public class AddImageDto
4 | {
5 | public IFormFile? Image { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/Enums/GroupRole.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Enums
2 | {
3 | public enum GroupRole
4 | {
5 | User,
6 | Moderator,
7 | Admin
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Groups/GroupEditDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Groups
2 | {
3 | public class GroupEditDto
4 | {
5 | public required string GroupName { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/Enums/ProjectStatus.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Enums
2 | {
3 | public enum ProjectStatus
4 | {
5 | OnHold,
6 | InProgress,
7 | Done
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/ForgotPasswordDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class ForgotPasswordDto
4 | {
5 | public required string Email { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/EmailSettings.cs:
--------------------------------------------------------------------------------
1 | namespace backend
2 | {
3 | public class EmailSettings
4 | {
5 | public string? Email { get; set; }
6 | public string? Password { get; set; }
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IEmailRepository.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Interface
2 | {
3 | public interface IEmailRepository
4 | {
5 | Task SendEmailAsync(string email, string subject, string message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/backend/backend/Enums/ProjectTaskStatus.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Enums
2 | {
3 | public enum ProjectTaskStatus
4 | {
5 | Backlog,
6 | InProgress,
7 | ForReview,
8 | Closed
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/View/NotesView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const noteSchema = z.object({
4 | title: z.string().min(1),
5 | content: z.string(),
6 | });
7 |
8 | export type noteModel = z.infer;
9 |
--------------------------------------------------------------------------------
/frontend/.eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from '@eslint/js';
4 | import tseslint from 'typescript-eslint';
5 |
6 | export default tseslint.config(
7 | eslint.configs.recommended,
8 | ...tseslint.configs.recommended,
9 | );
--------------------------------------------------------------------------------
/backend/backend/Dto/Notes/EditNoteDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Notes
2 | {
3 | public class EditNoteDto
4 | {
5 | public required string Title { get; set; }
6 | public required string Content { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/LoginUserDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class LoginUserDto
4 | {
5 | public required string Email { get; set; }
6 | public required string Password { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/backend/backend/Dto/CalendarEvents/EventPaginationDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.CalendarEvents
2 | {
3 | public class EventPaginationDto
4 | {
5 | public string? from { get; set; }
6 | public string? to { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectView/UpdateGroup/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const groupSchema = z
4 | .object({
5 | groupName: z.string().min(1),
6 | });
7 |
8 | export type groupModel = z.infer;
9 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/TodoTask/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const todoSchema = z.object({
4 | title: z.string().min(1),
5 | description: z.string().optional(),
6 | });
7 |
8 | export type todoModel = z.infer;
9 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTasks/ProjectTaskStatusDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.ProjectTasks
4 | {
5 | public class ProjectTaskStatusDto
6 | {
7 | public ProjectTaskStatus Status { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Token/AccessTokenDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Token
2 | {
3 | public class AccessTokenDto
4 | {
5 | public required string AccessToken { get; set; }
6 | public required string RefreshToken { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/View/LoginView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const loginFormSchema = z.object({
4 | email: z.string().min(1).email(),
5 | password: z.string().min(1),
6 | });
7 |
8 | export type loginFormSchema = z.infer;
9 |
--------------------------------------------------------------------------------
/backend/backend/Exceptions/NotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Exceptions
2 | {
3 | public class NotFoundException : Exception
4 | {
5 | public NotFoundException(string message) : base(message)
6 | {
7 |
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/backend/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning",
6 | "Microsoft.AspNetCore.DataProtection": "None"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/Section/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './ColorEditSection';
2 | export * from './ColorSection';
3 | export * from './LanguageSection';
4 | export * from './ReminderSection';
5 | export * from './RouteSection';
6 | export * from './ThemeSection';
7 |
--------------------------------------------------------------------------------
/backend/backend/AuthSettings.cs:
--------------------------------------------------------------------------------
1 | namespace backend
2 | {
3 | public class AuthSettings
4 | {
5 | public string? JwtKey { get; set; }
6 | public int JwtExpireMinutes { get; set; }
7 | public string? JwtIssuer { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IUserContextRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace backend.Interface
4 | {
5 | public interface IUserContextRepository
6 | {
7 | int? GetUserId { get; }
8 | ClaimsPrincipal User { get; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const projectSchema = z.object({
4 | title: z.string().min(1),
5 | description: z.string().optional(),
6 | color: z.string(),
7 | });
8 |
9 | export type projectModel = z.infer;
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Dashboard/DashboardNoteDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Dashboard
2 | {
3 | public class DashboardNoteDto
4 | {
5 | public int NoteID { get; set; }
6 | public string Title { get; set; }
7 | public string ShortDescription { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Todos/AddTodoDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Todos
2 | {
3 | public class AddTodoDto
4 | {
5 | public int ProjectTodoID { get; set; }
6 | public required string Title { get; set; }
7 | public required string Description { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/src/config/constants.ts:
--------------------------------------------------------------------------------
1 |
2 | const apiUrl = import.meta.env.VITE_API_URL;
3 |
4 | export const BASE_URL = apiUrl ?? "https://localhost:7086";
5 |
6 | export const SIDEBAR_WIDTH = 58;
7 |
8 | export const REFRESH_TOKEN = 'Refresh-token';
9 |
10 | export const AUTH_TOKEN = 'Auth-token';
11 |
12 |
--------------------------------------------------------------------------------
/frontend/lingui.config.ts:
--------------------------------------------------------------------------------
1 | import type { LinguiConfig } from "@lingui/conf";
2 |
3 | const config: LinguiConfig = {
4 | locales: ["en", "pl"],
5 | catalogs: [
6 | {
7 | path: "/src/locales/{locale}",
8 | include: ["src"],
9 | },
10 | ],
11 | };
12 |
13 | export default config;
--------------------------------------------------------------------------------
/backend/backend/Dto/Pagination/PaginationRequestDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Pagination
2 | {
3 | public class PaginationRequestDto
4 | {
5 | public int PageNumber { get; set; } = 1;
6 | public int PageSize { get; set; } = 5;
7 | public string? Filter { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTodo/AddProjectTodoDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.ProjectTodo
2 | {
3 | public class AddProjectTodoDto
4 | {
5 | public required string Title { get; set; }
6 | public required string Description { get; set; }
7 | public required string Color { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/ResetPasswordDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class ResetPasswordDto
4 | {
5 | public required string ResetToken { get; set; }
6 | public required string Password { get; set; }
7 | public required string PasswordConfirm { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Todos/TodoDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Todos
2 | {
3 | public class TodoDto
4 | {
5 | public int TodoID { get; set; }
6 | public required string Title { get; set; }
7 | public required string Description { get; set; }
8 | public bool IsDone { get; set; }
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/UserDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class UserDto
4 | {
5 | public required string Username { get; set; }
6 | public required string Email { get; set; }
7 | public string? GroupName { get; set; }
8 | public string? Image { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Dashboard/DashboardTodoDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Dashboard
2 | {
3 | public class DashboardTodoDto
4 | {
5 |
6 | public int CompletionPercentage { get; set; }
7 | public int TotalTodos { get; set; }
8 | public int CompletedTodos { get; set; }
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Groups/GroupByIdDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Models;
2 |
3 | namespace backend.Dto.Groups
4 | {
5 | public class GroupByIdDto
6 | {
7 | public int GroupID { get; set; }
8 | public required string GroupName { get; set; }
9 | public virtual List? Users { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/api/pagination.ts:
--------------------------------------------------------------------------------
1 | export interface PaginationRequest {
2 | pageNumber: number;
3 | pageSize: number;
4 | filter? : string;
5 | }
6 |
7 | export interface PaginationResponse {
8 | items: T[];
9 | totalItems: number;
10 | totalPages: number;
11 | pageNumber: number;
12 | pageSize: number;
13 | }
14 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Notes/AllNotesDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Notes
2 | {
3 | public class AllNotesDto
4 | {
5 |
6 | public int NoteID { get; set; }
7 | public required string Title { get; set; }
8 | public required string Content { get; set; }
9 | public DateTime CreatedAt { get; set; }
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/UpdateUserDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class UpdateUserDto
4 | {
5 | public required string Username { get; set; }
6 | public required string Email { get; set; }
7 | public string? OldPassword { get; set; }
8 | public string? Password { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/backend/backend/Models/GroupRole.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class GroupRole
4 | {
5 | public int UserId { get; set; }
6 | public User? User { get; set; }
7 | public int GroupId { get; set; }
8 | public Group? Group { get; set; }
9 | public Enums.GroupRole Role { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/backend/backend/Interface/INotificationRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Notifications;
2 | using backend.Models;
3 |
4 | namespace backend.Interface
5 | {
6 | public interface INotificationRepository
7 | {
8 | Task GetNotifications();
9 | Task UpdateNotifications();
10 | Task MarkAsRead(int id);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/src/utils/dateTime.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | export const formatDate = (inputDate: string | number | Date) => {
4 | const date = new Date(inputDate);
5 | const dateFormat = new Intl.DateTimeFormat('pl-PL', {
6 | dateStyle: 'short',
7 | timeStyle: 'short',
8 | timeZone: 'UTC',
9 | });
10 |
11 | return dateFormat.format(date);
12 | };
--------------------------------------------------------------------------------
/backend/backend/Interface/IUserPreferencesRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Models;
2 | using backend.Dto.UserPreferences;
3 |
4 | namespace backend.Interface
5 | {
6 | public interface IUserPreferencesRepository
7 | {
8 | Task GetSettings();
9 | Task UpdateUserPreferences(UserPreferencesDto userPreferences);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/utils/debounce.ts:
--------------------------------------------------------------------------------
1 | export function debounce(
2 | func: (...args: Params) => any,
3 | timeout: number,
4 | ): (...args: Params) => void {
5 | let timer: NodeJS.Timeout;
6 | return (...args: Params) => {
7 | clearTimeout(timer);
8 | timer = setTimeout(() => {
9 | func(...args);
10 | }, timeout);
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Users/RegisterUserDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Users
2 | {
3 | public class RegisterUserDto
4 | {
5 | public required string Username { get; set; }
6 | public required string Email { get; set; }
7 | public required string Password { get; set; }
8 | public required string PasswordConfirm { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/frontend/src/utils/deriveBarColor.ts:
--------------------------------------------------------------------------------
1 | import { ProjectStatus } from "~/api/Projects/api";
2 |
3 | export const deriveBarColor = (completion: ProjectStatus) => {
4 | switch (completion) {
5 | case ProjectStatus.OnHold:
6 | return 'inherit';
7 | case ProjectStatus.Done:
8 | return 'success';
9 | default:
10 | return 'info';
11 | }
12 | };
--------------------------------------------------------------------------------
/frontend/src/utils/deriveProjectStatus.ts:
--------------------------------------------------------------------------------
1 | import { ProjectStatus } from "~/api/Projects/api";
2 |
3 | export const deriveProjectStatus = (status: ProjectStatus) => {
4 | switch (status) {
5 | case ProjectStatus.OnHold:
6 | return 'on Hold';
7 | case ProjectStatus.Done:
8 | return 'Done';
9 | default:
10 | return 'in Progress';
11 | }
12 | };
--------------------------------------------------------------------------------
/backend/backend/Dto/CalendarEvents/CalendarEventDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.CalendarEvents
2 | {
3 | public class CalendarEventDto
4 | {
5 | public required string Title { get; set; }
6 | public required string Description { get; set; }
7 | public required string DateTime { get; set; }
8 | public required string EventColor { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Projects/AddProjectDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.Projects
4 | {
5 | public class AddProjectDto
6 | {
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public DateTime Deadline { get; set; }
10 | public ProjectStatus Status { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Projects/EditProjectDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.Projects
4 | {
5 | public class EditProjectDto
6 | {
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public DateTime Deadline { get; set; }
10 | public ProjectStatus Status { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/src/View/LogoutView/LogoutView.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { mutateUserLogout } from '~/api/User/query';
3 | import { LoadingView } from '../LoadingView/LoadingView';
4 |
5 | export const LogoutView = () => {
6 | const { mutateAsync } = mutateUserLogout();
7 |
8 | useEffect(() => {
9 | mutateAsync();
10 | }, []);
11 |
12 | return ;
13 | };
14 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Pagination/PaginationResponseDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Pagination
2 | {
3 | public class PaginationResponseDto
4 | {
5 | public List? Items { get; set; }
6 | public int TotalItems { get; set; }
7 | public int TotalPages { get; set; }
8 | public int PageNumber { get; set; }
9 | public int PageSize { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/View/LoadingView/LoadingView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, CircularProgress } from '@mui/material';
2 |
3 | export const LoadingView = () => {
4 | return (
5 |
11 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/frontend/src/View/ProjectView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 | import { ProjectStatus } from '~/api/Projects/api';
3 |
4 | export const projectSchema = z
5 | .object({
6 | title: z.string().min(1),
7 | description: z.string().optional(),
8 | deadline: z.date(),
9 | status: z.nativeEnum(ProjectStatus),
10 | });
11 |
12 | export type projectModel = z.infer;
13 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Tauri + React + Typescript
2 |
3 | This template should help get you started developing with Tauri, React and Typescript in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | - [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
8 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTasks/ProjectTaskDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.ProjectTasks
4 | {
5 | public class ProjectTaskDto
6 | {
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public required string Category { get; set; }
10 | public ProjectTaskStatus Status { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTodo/ProjectTodoDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Todos;
2 |
3 | namespace backend.Dto.ProjectTodo
4 | {
5 | public class ProjectTodoDto
6 | {
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public required string Color { get; set; }
10 | public ICollection? Todos { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/src/api/Image/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 |
3 | export const postImage = async (data: FormData) => {
4 | return await axiosInstance.post('/api/images', data, {
5 | headers: {
6 | 'Content-Type': 'multipart/form-data',
7 | },
8 | });
9 | };
10 |
11 | export const deleteImage = async (data: string) => {
12 | return await axiosInstance.delete(`/api/images/${data}`);
13 | };
14 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectAvatar/ProjectAvatar.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar } from '@mui/material';
2 | import { stringAvatar } from '~/utils/userAvatar';
3 |
4 | type Props = {
5 | name: string;
6 | };
7 |
8 | export const ProjectAvatar = ({ name }: Props) => {
9 | return (
10 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/src/component/DropBox/DropBox.tsx:
--------------------------------------------------------------------------------
1 | import { alpha, Box } from '@mui/material';
2 |
3 | export const DropBox = () => (
4 | ({
6 | width: '100%',
7 | height: '50px',
8 | backgroundColor: theme.palette.action.hover,
9 | borderRadius: 2,
10 | border: '2px dashed',
11 | borderColor: alpha(theme.palette.action.hover, 0.25),
12 | })}
13 | >
14 | );
15 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Zenith
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/backend/backend/Dto/CalendarEvents/AllCalendarEventsDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.CalendarEvents
2 | {
3 | public class AllCalendarEventsDto
4 | {
5 | public int EventID { get; set; }
6 | public required string Title { get; set; }
7 | public required string Description { get; set; }
8 | public DateTime DateTime { get; set; }
9 | public required string EventColor { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Groups/GroupUsersDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Groups
2 | {
3 | public class GroupUsersDto
4 | {
5 | public int UserID { get; set; }
6 | public required string Username { get; set; }
7 | public required string Email { get; set; }
8 | public Enums.GroupRole UserRole { get; set; }
9 | public bool IsMe { get; set; }
10 | public string? Image { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/backend/docker-compose.yaml:
--------------------------------------------------------------------------------
1 |
2 | services:
3 | backend:
4 | container_name: backend
5 | build: .
6 | env_file: "./backend/.env"
7 | ports:
8 | - "5000:8080"
9 | depends_on:
10 | - mssql
11 | mssql:
12 | container_name: mssql
13 | image: mcr.microsoft.com/mssql/server:2022-latest
14 | environment:
15 | - ACCEPT_EULA=Y
16 | - MSSQL_SA_PASSWORD=Password1*
17 | ports:
18 | - "1433:1433"
--------------------------------------------------------------------------------
/frontend/src/Theme/Animations.ts:
--------------------------------------------------------------------------------
1 | import { keyframes } from '@mui/material';
2 |
3 | export const pulseScale = keyframes`
4 | 0% {
5 | transform: scale(1);
6 | }
7 | 50% {
8 | transform: scale(1.05);
9 | }
10 | 100% {
11 | transform: scale(1);
12 | }
13 | `;
14 |
15 | export const pulseOpacity = keyframes`
16 | 0% {
17 | opacity: 0:2;
18 | }
19 | 50% {
20 | opacity: 0.8;
21 | }
22 | 100% {
23 | opacity: 0:2;
24 | }
25 | `;
26 |
--------------------------------------------------------------------------------
/backend/backend/Models/Todo.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class Todo
4 | {
5 | public int TodoID { get; set; }
6 | public int ProjectTodoID { get; set; }
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 |
10 | public bool IsDone { get; set; }
11 |
12 |
13 | public virtual ProjectTodo? ProjectTodo { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/backend/backend/Dto/GroupProjects/AddGroupProjectDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.GroupProjects
4 | {
5 | public class AddGroupProjectDto
6 | {
7 | public required string Title { get; set; }
8 | public int GroupID { get; set; }
9 | public required string Description { get; set; }
10 | public DateTime Deadline { get; set; }
11 | public ProjectStatus Status { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IKanbanTaskRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Models;
2 |
3 | namespace backend.Interface
4 | {
5 | public interface IKanbanTaskRepository
6 | {
7 | KanbanTask GetKanbanTaskById(int kanbanTaskId);
8 | List GetAllKanbanTasks();
9 | void AddKanbanTask(KanbanTask kanbanTask);
10 | void UpdateKanbanTask(KanbanTask kanbanTask);
11 | void DeleteKanbanTask(int kanbanTaskId);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTasks/AddProjectTaskDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.ProjectTasks
4 | {
5 | public class AddProjectTaskDto
6 | {
7 | public int ProjectID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public required string Category { get; set; }
11 | public ProjectTaskStatus Status { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 | import { ProjectStatus } from '~/api/Projects/api';
3 |
4 | export const groupProjectSchema = z
5 | .object({
6 | title: z.string().min(1),
7 | groupID: z.number().optional(),
8 | description: z.string().optional(),
9 | deadline: z.date(),
10 | status: z.nativeEnum(ProjectStatus),
11 | });
12 |
13 | export type groupProjectModel = z.infer;
14 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTasks/ProjectTaskShortDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.ProjectTasks
4 | {
5 | public class ProjectTaskShortDto
6 | {
7 | public int ProjectTaskID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public required string Category { get; set; }
11 | public ProjectTaskStatus Status { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/backend/backend/Models/Group.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class Group
4 | {
5 | public int GroupID { get; set; }
6 | public required string GroupName { get; set; }
7 | public string? InviteToken { get; set; }
8 | public DateTime? TokenResetTime { get; set; }
9 |
10 | public required virtual ICollection Users { get; set; }
11 | public virtual ICollection? GroupProjects { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/src/utils/useGroupRoles.ts:
--------------------------------------------------------------------------------
1 | import { GroupRole } from '~/api/Group/api';
2 |
3 | export const getGroupRole = (roleId: number) => {
4 | switch (roleId) {
5 | case 1:
6 | return 'Moderator';
7 | case 2:
8 | return 'Admin';
9 | default:
10 | return 'User';
11 | }
12 | };
13 |
14 | export const isAdmin = (roleId: GroupRole) => {
15 | switch (roleId) {
16 | case 2:
17 | return true;
18 | default:
19 | return false;
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/frontend/src/component/Main/Main.tsx:
--------------------------------------------------------------------------------
1 | import { Box, BoxProps } from '@mui/material';
2 |
3 | export const Main = ({ children, sx, ...rest }: BoxProps) => {
4 | return (
5 | ({
7 | flex: 1,
8 | width: '100%',
9 | display: 'flex',
10 | flexDirection: 'column',
11 | padding: theme.spacing(3),
12 | paddingLeft: theme.spacing(34),
13 | })}
14 | {...rest}
15 | >
16 | {children}
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/backend/backend/Interface/ICalendarEventRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.CalendarEvents;
2 | using backend.Models;
3 |
4 | namespace backend.Interface
5 | {
6 | public interface ICalendarEventRepository
7 | {
8 | Task> GetAllEventsBetween(string from, string to, Dictionary colors);
9 | Task AddEvent(CalendarEventDto dto);
10 | Task UpdateEvent(CalendarEventDto dto, int eventId);
11 | Task DeleteEvent(int eventId);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/src/View/ProjectTask/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 | import { ProjectTaskStatus } from '~/api/Projects/api';
3 |
4 | export const taskSchema = z
5 | .object({
6 | projectID: z.number(),
7 | title: z.string().min(1),
8 | description: z.string().min(1),
9 | category: z.enum(['Note' , 'Email' , 'Meeting' , 'Research' , 'Design' , 'Development' , 'Maintenance']),
10 | status: z.nativeEnum(ProjectTaskStatus),
11 | });
12 |
13 | export type taskModel = z.infer;
14 |
--------------------------------------------------------------------------------
/backend/backend/Dto/GroupProjectTasks/AddGroupProjectTaskDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.GroupProjectTasks
4 | {
5 | public class AddGroupProjectTaskDto
6 | {
7 | public int ProjectID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public required string Category { get; set; }
11 | public ProjectTaskStatus Status { get; set; }
12 | public int UserId { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/backend/backend/Dto/UserPreferences/UserPreferencesDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.UserPreferences
2 | {
3 | public class UserPreferencesDto
4 | {
5 | public required string Theme { get; set; }
6 | public required string Color { get; set; }
7 | public required string Language { get; set; }
8 | public int Reminder { get; set; }
9 | public required Dictionary Routes { get; set; }
10 | public required Dictionary Colors { get; set; }
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/backend/backend/Models/Note.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class Note
4 | {
5 | public int NoteID { get; set; }
6 | public int UserID { get; set; }
7 | public required string Title { get; set; }
8 | public required string Content { get; set; }
9 | public DateTime CreatedAt { get; set; }
10 | public string? ShareToken { get; set; }
11 | public DateTime? TokenResetTime { get; set; }
12 |
13 | public virtual User? User { get; set; }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/backend/Store/StringGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace backend.Store
5 | {
6 | public static class StringGenerator
7 | {
8 | public static string RandomString(int length)
9 | {
10 | const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
11 | Random random = new Random();
12 | return new string(Enumerable.Repeat(chars, length)
13 | .Select(s => s[random.Next(s.Length)]).ToArray());
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/src/utils/useCurrentDate.ts:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs';
2 |
3 | export const useCurrentDate = (passedMonth: number) => {
4 | const month = passedMonth
5 | const year = dayjs().year();
6 | const firstDay = dayjs(new Date(year, month, 1)).day();
7 |
8 | let monthCount = 0 - firstDay;
9 | const days = new Array(5).fill([]).map(() => {
10 | return new Array(7).fill(null).map(() => {
11 | monthCount++;
12 | return dayjs(new Date(year, month, monthCount));
13 | });
14 | });
15 | return days;
16 | };
17 |
--------------------------------------------------------------------------------
/backend/backend/Dto/GroupProjectTasks/GroupProjectTaskDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.GroupProjectTasks
4 | {
5 | public class GroupProjectTaskDto
6 | {
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public required string Category { get; set; }
10 | public ProjectTaskStatus Status { get; set; }
11 | public required string User { get; set; }
12 | public string? UserImage { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Projects/AllProjectsDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.Projects
4 | {
5 | public class AllProjectsDto
6 | {
7 | public int ProjectID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public DateTime Deadline { get; set; }
11 | public ProjectStatus Status { get; set; }
12 | public float Completion { get; set; }
13 | public bool isOutdated { get; set; }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/src/View/RegisterView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const registerFormSchema = z
4 | .object({
5 | id: z.number(),
6 | username: z.string().min(1),
7 | email: z.string().min(1).email(),
8 | password: z.string().min(8),
9 | passwordConfirm: z.string().min(8),
10 | })
11 | .refine((item) => item.password === item.passwordConfirm, {
12 | message: "Passwords don't match",
13 | path: ['passwordConfirm'],
14 | });
15 |
16 | export type registerFormSchema = z.infer;
17 |
--------------------------------------------------------------------------------
/backend/backend/Interface/ITodoRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Todos;
2 | using backend.Models;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace backend.Interface
6 | {
7 | public interface ITodoRepository
8 | {
9 | Task> GetAllTodos(int projectId);
10 | Task AddTodo(AddTodoDto todo);
11 | Task UpdateTodo(AddTodoDto todo, int todoId);
12 | Task DeleteTodo(int todoId);
13 | Task ToggleDone(ToggleTodoDto dto, int todoId);
14 | Task ToggleProject(int projectId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/backend/Dto/GroupProjects/AllGroupProjectsDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.GroupProjects
4 | {
5 | public class AllGroupProjectsDto
6 | {
7 | public int GroupProjectID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public DateTime Deadline { get; set; }
11 | public ProjectStatus Status { get; set; }
12 | public float Completion { get; set; }
13 | public bool isOutdated { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectTaskView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 | import { ProjectTaskStatus } from '~/api/Projects/api';
3 |
4 | export const taskSchema = z
5 | .object({
6 | projectID: z.number(),
7 | title: z.string().min(1),
8 | description: z.string().min(1),
9 | userId: z.number(),
10 | category: z.enum(['Note' , 'Email' , 'Meeting' , 'Research' , 'Design' , 'Development' , 'Maintenance']),
11 | status: z.nativeEnum(ProjectTaskStatus),
12 | });
13 |
14 | export type taskModel = z.infer;
15 |
--------------------------------------------------------------------------------
/backend/backend/Models/ProjectTodo.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class ProjectTodo
4 | {
5 | public int ProjectTodoID { get; set; }
6 | public int UserID { get; set; }
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public required string Color { get; set; }
10 | public bool IsDone { get; set; } = false;
11 |
12 | public virtual User? User { get; set; }
13 | public virtual ICollection? Todos { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupView.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@mui/material';
2 | import { NoGroupView } from './NoGroupView';
3 | import { getIsInGroup } from '~/api/Group/query';
4 | import { LoadingView } from '../LoadingView/LoadingView';
5 | import { GroupProjectView } from './GroupProjectView/GroupProjectView';
6 |
7 | export const GroupView = () => {
8 | const { data: group, isLoading } = getIsInGroup();
9 |
10 | if (isLoading) {
11 | return ;
12 | }
13 |
14 | return {group ? : };
15 | };
16 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Projects/ProjectDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.ProjectTasks;
2 | using backend.Enums;
3 | using backend.Models;
4 |
5 | namespace backend.Dto.Projects
6 | {
7 | public class ProjectDto
8 | {
9 | public int ProjectID { get; set; }
10 | public required string Title { get; set; }
11 | public required string Description { get; set; }
12 | public DateTime Deadline { get; set; }
13 | public ProjectStatus Status { get; set; }
14 | public ICollection? ProjectTasks { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App';
4 | import { QueryClientProvider } from '@tanstack/react-query';
5 | import { AuthProvider } from './context/AuthContext';
6 | import { queryClient } from './api/api';
7 |
8 | ReactDOM.createRoot(document.getElementById('root')!).render(
9 |
10 |
11 |
12 |
13 |
14 |
15 | ,
16 | );
17 |
--------------------------------------------------------------------------------
/backend/backend/Dto/ProjectTodo/AllProjectsTodoDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.ProjectTodo
2 | {
3 | public class ProjectsTodoDto
4 | {
5 | public List Projects { get; set; } = new List();
6 | }
7 | public class AllProjectsTodoDto
8 | {
9 | public int ProjectTodoID { get; set; }
10 | public required string Title { get; set; }
11 | public required string Description { get; set; }
12 | public required string Color { get; set; }
13 | public bool IsDone { get; set; }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/src/component/UserAvatar/UserAvatar.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar } from '@mui/material';
2 | import { BASE_URL } from '~/config/constants';
3 | import { stringAvatar } from '~/utils/userAvatar';
4 |
5 | type Props = {
6 | image: string | null;
7 | username: string;
8 | };
9 |
10 | export const UserAvatar = ({ username, image }: Props) => {
11 | return (
12 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IProjectRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Pagination;
2 | using backend.Dto.Projects;
3 | using backend.Models;
4 |
5 | namespace backend.Interface
6 | {
7 | public interface IProjectRepository
8 | {
9 | Task GetProjectById(int projectId);
10 | Task> GetAllProjects(PaginationRequestDto paginationRequest);
11 | Task AddProject(AddProjectDto project);
12 | Task UpdateProject(EditProjectDto project, int projectId);
13 | Task DeleteProject(int projectId);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/backend/backend/Models/KanbanTask.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class KanbanTask
4 | {
5 | public int KanbanTaskID { get; set; }
6 | public int UserID { get; set; }
7 | public required string Title { get; set; }
8 | public required string Description { get; set; }
9 | public int Priority { get; set; }
10 | public DateTime Deadline { get; set; }
11 | public required string Category { get; set; }
12 | public required string Status { get; set; }
13 |
14 | public virtual User? User { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/backend/Models/ProjectTask.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Models
4 | {
5 | public class ProjectTask
6 | {
7 | public int ProjectTaskID { get; set; }
8 | public int ProjectID { get; set; }
9 | public required string Title { get; set; }
10 | public required string Description { get; set; }
11 | public required string Category { get; set; }
12 | public ProjectTaskStatus Status { get; set; }
13 | public DateTime EditTime { get; set; }
14 |
15 | public virtual Project? Project { get; set; }
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backend/backend/Models/UserPreferences.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class UserPreferences
4 | {
5 | public int PreferencesID { get; set; }
6 | public int UserID { get; set; }
7 | public required string Theme { get; set; }
8 | public required string Color { get; set; }
9 | public required string Language { get; set; }
10 | public int Reminder { get; set; }
11 | public required string Routes { get; set; }
12 | public string Colors { get; set; }
13 |
14 |
15 | public virtual User? User { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/frontend/src/View/CalendarView/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const eventSchema = z.object({
4 | title: z.string().min(1),
5 | description: z.string(),
6 | dateTime: z.string(),
7 | eventColor: z.string().min(1),
8 | });
9 |
10 | export type eventModel = z.infer;
11 |
12 |
13 | export const colorSchema = z.object({
14 | colors: z.object({
15 | Purple: z.boolean(),
16 | Red: z.boolean(),
17 | Green: z.boolean(),
18 | Blue: z.boolean(),
19 | Yellow: z.boolean(),
20 | }),
21 | });
22 |
23 | export type ColorModel = z.infer;
24 |
--------------------------------------------------------------------------------
/frontend/src/utils/useDebouncedValue.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { debounce } from 'lodash';
3 |
4 | type Props = {
5 | value: any;
6 | delay: number;
7 | };
8 | export const useDebouncedValue = ({ value, delay }: Props) => {
9 | const [debouncedValue, setDebouncedValue] = useState(value);
10 |
11 | useEffect(() => {
12 | const handler = debounce(() => {
13 | setDebouncedValue(value);
14 | }, delay);
15 |
16 | handler();
17 |
18 | return () => {
19 | handler.cancel();
20 | };
21 | }, [value, delay]);
22 |
23 | return debouncedValue;
24 | };
25 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IProjectTaskRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.ProjectTasks;
2 | using backend.Models;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace backend.Interface
6 | {
7 | public interface IProjectTaskRepository
8 | {
9 | Task GetProjectTaskById(int projectTaskId);
10 | Task AddProjectTask(AddProjectTaskDto projectTask);
11 | Task UpdateProjectTask(ProjectTaskDto projectTask, int projectTaskId);
12 |
13 | Task ChangeProjectTaskStatus(ProjectTaskStatusDto status, int projectTaskId);
14 | Task DeleteProjectTask(int projectTaskId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IProjectTodoRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Pagination;
2 | using backend.Dto.ProjectTodo;
3 | using backend.Models;
4 |
5 | namespace backend.Interface
6 | {
7 | public interface IProjectTodoRepository
8 | {
9 | Task> GetAllProjects(bool isDone, PaginationRequestDto paginationRequest);
10 | Task AddProject(AddProjectTodoDto project);
11 | Task UpdateProject(AddProjectTodoDto project, int projectId);
12 | Task DeleteProject(int projectId);
13 | Task GetProjectById(int projectId);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/api/Settings/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 |
3 | export interface Settings {
4 | theme: string;
5 | color: string;
6 | language: string;
7 | reminder: number;
8 | routes: { [routeName: string]: boolean };
9 | colors: { [color: string]: string };
10 | }
11 |
12 | export const querySettings = async () => {
13 | const response = await axiosInstance.get('/api/settings/getSettings');
14 | return response.data as Settings;
15 | };
16 |
17 | export const editSettings = async (preferences: Settings) => {
18 | return await axiosInstance.patch(`/api/settings/updateSettings`, preferences);
19 | };
20 |
--------------------------------------------------------------------------------
/backend/backend/Dto/GroupProjectTasks/GroupProjectTaskShortDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.GroupProjectTasks
4 | {
5 | public class GroupProjectTaskShortDto
6 | {
7 | public int ProjectTaskID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public required string Category { get; set; }
11 | public ProjectTaskStatus Status { get; set; }
12 | public required string User { get; set; }
13 | public string? UserImage { get; set; }
14 | public bool CanEdit { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/backend/Models/CalendarEvent.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class CalendarEvent
4 | {
5 | public int EventID { get; set; }
6 | public int UserID { get; set; }
7 | public int? NotificationID { get; set; }
8 | public required string Title { get; set; }
9 | public required string Description { get; set; }
10 | public DateTime DateTime { get; set; }
11 | public required string EventColor { get; set; }
12 |
13 | public virtual User? User { get; set; }
14 | public virtual CalendarEventNotification? Notification { get; set; }
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IDashboardRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.CalendarEvents;
2 | using backend.Dto.Dashboard;
3 |
4 | namespace backend.Interface
5 | {
6 | public interface IDashboardRepository
7 | {
8 | Task GetTodoCompletion();
9 | Task> GetClosestEvents(int maxEvents = 5);
10 | Task> GetLatestNotes(int maxNotes = 5);
11 | Task GetApproachingDeadlineProjects(int daysThreshold = 7);
12 | Task GetApproachingDeadlineGroupProjects(int daysThreshold = 7);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IGroupProjectTaskRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.GroupProjectTasks;
2 | using backend.Dto.ProjectTasks;
3 | using backend.Models;
4 |
5 | namespace backend.Interface
6 | {
7 | public interface IGroupProjectTaskRepository
8 | {
9 | Task GetProjectTaskById(int groupProjectTaskId);
10 | Task AddProjectTask(AddGroupProjectTaskDto dto);
11 | Task UpdateProjectTask(AddGroupProjectTaskDto dto, int groupProjectTaskId);
12 | Task ChangeProjectTaskStatus(ProjectTaskStatusDto status, int groupProjectTaskId);
13 | Task DeleteProjectTask(int groupProjectTaskId);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/utils/useCurrentWeek.ts:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs';
2 | type Props= {
3 | passedMonth: number;
4 | week: number;
5 | }
6 |
7 | export const useCurrentWeek = ({passedMonth, week }: Props) => {
8 | const month = passedMonth
9 | const year = dayjs().year();
10 | const firstDay = dayjs(new Date(year, month, 1)).day();
11 |
12 | let monthCount = 0 - firstDay;
13 | const days =
14 | new Array(5).fill([]).map(() => {
15 | return new Array(7).fill(null).map(() => {
16 | monthCount++;
17 | return dayjs(new Date(year, month, monthCount));
18 | });
19 | });
20 |
21 | return days[week];
22 | };
23 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IGroupProjectRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.GroupProjects;
2 | using backend.Dto.Pagination;
3 | using backend.Dto.Projects;
4 | using backend.Models;
5 |
6 | namespace backend.Interface
7 | {
8 | public interface IGroupProjectRepository
9 | {
10 | Task GetGroupProjectById(int projectId);
11 | Task> GetAllGroupProjects(PaginationRequestDto paginationRequest);
12 | Task AddGroupProject(AddGroupProjectDto dto);
13 | Task UpdateGroupProject(EditProjectDto dto, int projectId);
14 | Task DeleteGroupProject(int projectId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/backend/Interface/INoteRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Notes;
2 | using backend.Dto.Pagination;
3 | using backend.Models;
4 | using Task = System.Threading.Tasks.Task;
5 |
6 | namespace backend.Interface
7 | {
8 | public interface INoteRepository
9 | {
10 | Task GetNoteById(int noteId);
11 | Task> GetAllNotes(PaginationRequestDto pagination);
12 | Task AddNote();
13 | Task UpdateNote(EditNoteDto dto, int noteId);
14 | Task DeleteNote(int noteId);
15 |
16 | Task GetShareToken(int noteId);
17 | Task GetNoteFromToken(string tokenId);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/View/RegisterView/PasswordRenew/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const forgotPasswordSchema = z.object({
4 | email: z.string().email().min(1),
5 | });
6 |
7 | export type forgotPasswordModel = z.infer;
8 |
9 | export const resetPasswordSchema = z
10 | .object({
11 | resetToken: z.string().min(16).max(16),
12 | password: z.string().min(8),
13 | passwordConfirm: z.string().min(8),
14 | })
15 | .refine((item) => item.password === item.passwordConfirm, {
16 | message: "Passwords don't match",
17 | path: ['passwordConfirm'],
18 | });
19 |
20 | export type resetPasswordModel = z.infer;
21 |
--------------------------------------------------------------------------------
/frontend/src/utils/userAvatar.ts:
--------------------------------------------------------------------------------
1 | export const stringAvatar = (name: string) => {
2 | return {
3 | sx: {
4 | bgcolor: stringToColor(name),
5 | },
6 | children: `${name.split(' ')[0][0].toLocaleUpperCase()}`,
7 | };
8 | }
9 |
10 | export const stringToColor = (string: string) => {
11 | let hash = 0;
12 | let i;
13 |
14 | for (i = 0; i < string.length; i += 1) {
15 | hash = string.charCodeAt(i) + ((hash << 5) - hash);
16 | }
17 |
18 | let color = '#';
19 |
20 | for (i = 0; i < 3; i += 1) {
21 | const value = (hash >> (i * 8)) & 0xff;
22 | color += `00${value.toString(16)}`.slice(-2);
23 | }
24 |
25 | return color;
26 | }
27 |
--------------------------------------------------------------------------------
/backend/backend/Models/GroupProjectTask.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Models
4 | {
5 | public class GroupProjectTask
6 | {
7 | public int GroupProjectTaskID { get; set; }
8 | public int GroupProjectID { get; set; }
9 | public int UserID { get; set; }
10 | public required string Title { get; set; }
11 | public required string Description { get; set; }
12 | public required string Category { get; set; }
13 | public ProjectTaskStatus Status { get; set; }
14 | public DateTime EditTime { get; set; }
15 |
16 | public virtual GroupProject? GroupProject { get; set; }
17 | public virtual User? User { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/component/Clock/Clock.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography } from '@mui/material';
2 | import { useState } from 'react';
3 |
4 | export const Clock = () => {
5 | let time = new Date().toLocaleTimeString([], {
6 | hour: '2-digit',
7 | minute: '2-digit',
8 | });
9 |
10 | const [ctime, setTime] = useState(time);
11 | const UpdateTime = () => {
12 | time = new Date().toLocaleTimeString([], {
13 | hour: '2-digit',
14 | minute: '2-digit',
15 | });
16 | setTime(time);
17 | };
18 | setInterval(UpdateTime);
19 |
20 | return (
21 |
22 |
23 | {ctime}
24 |
25 |
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/backend/backend/Models/Project.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Models
4 | {
5 | public class Project
6 | {
7 | public int ProjectID { get; set; }
8 | public int UserID { get; set; }
9 | public int? NotificationID { get; set; }
10 | public required string Title { get; set; }
11 | public required string Description { get; set; }
12 | public DateTime Deadline { get; set; }
13 | public ProjectStatus Status { get; set; }
14 |
15 | public virtual User? User { get; set; }
16 | public virtual ICollection? ProjectTasks { get; set; }
17 | public virtual ProjectNotification? Notification { get; set; }
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Dashboard/DashboardGroupProjectDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.Dashboard
4 | {
5 | public class DashboardGroupProjectDto
6 | {
7 | public int GroupProjectID { get; set; }
8 | public string Title { get; set; }
9 | public DateTime Deadline { get; set; }
10 | public string Description { get; set; }
11 | public ProjectStatus Status { get; set; }
12 | public float CompletionPercentage { get; set; }
13 | }
14 |
15 | public class DashboardGroupProjectSummaryDto
16 | {
17 | public List Projects { get; set; }
18 | public Dictionary ProjectsCountByStatus { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IUserRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Token;
2 | using backend.Dto.Users;
3 | using backend.Models;
4 | using Task = System.Threading.Tasks.Task;
5 |
6 | namespace backend.Interface
7 | {
8 | public interface IUserRepository
9 | {
10 | Task GetUserById();
11 | List GetAllUsers();
12 | Task AddUser(RegisterUserDto user);
13 | Task UpdateUser(UpdateUserDto user);
14 | Task DeleteUser();
15 | Task LoginUser(LoginUserDto dto);
16 | Task VerifyEmail(string token);
17 | Task ForgotPassword(ForgotPasswordDto email);
18 | Task ResetPassword(ResetPasswordDto dto);
19 | Task LogoutUser();
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/backend/backend/Interface/IGroupRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Groups;
2 | using backend.Dto.Users;
3 | using backend.Models;
4 |
5 | namespace backend.Interface
6 | {
7 | public interface IGroupRepository
8 | {
9 | Task GetGroup();
10 | Task AddGroup(GroupEditDto group);
11 | Task UpdateGroup(GroupEditDto dto, int groupId);
12 | Task DeleteGroup(int groupId);
13 | Task GetInviteToken(int groupId);
14 | Task isInGroup();
15 | Task JoinGroup(TokenDto tokenId);
16 | Task LeaveGroup(LeaveGroupDto dto);
17 | Task ChangeRole(ChangeRoleDto dto);
18 | Task SetAdmin(ChangeRoleDto dto);
19 | Task GetOwnRole();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Dashboard/DashboardProjectDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Dto.Dashboard
4 | {
5 | public class DashboardProjectDto
6 | {
7 | public int ProjectID { get; set; }
8 | public string Title { get; set; }
9 | public DateTime Deadline { get; set; }
10 | public string Description { get; set; }
11 | public ProjectStatus Status { get; set; }
12 | public float CompletionPercentage { get; set; }
13 |
14 | }
15 |
16 | public class DashboardProjectSummaryDto
17 | {
18 | public List Projects { get; set; }
19 | public Dictionary ProjectsCountByStatus { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/backend/Models/GroupProject.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 |
3 | namespace backend.Models
4 | {
5 | public class GroupProject
6 | {
7 | public int GroupProjectID { get; set; }
8 | public int GroupID { get; set; }
9 | public int? NotificationID { get; set; }
10 | public required string Title { get; set; }
11 | public required string Description { get; set; }
12 | public DateTime Deadline { get; set; }
13 | public ProjectStatus Status { get; set; }
14 |
15 |
16 | public virtual ICollection? GroupProjectTasks { get; set; }
17 | public virtual Group? Group { get; set; }
18 | public virtual GroupProjectNotification? Notification { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 |
4 | use tauri::Manager;
5 |
6 | #[derive(Clone, serde::Serialize)]
7 | struct Payload {
8 | args: Vec,
9 | cwd: String,
10 | }
11 |
12 | fn main() {
13 | tauri::Builder::default()
14 | .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
15 | println!("{}, {argv:?}, {cwd}", app.package_info().name);
16 |
17 | app.emit_all("single-instance", Payload { args: argv, cwd })
18 | .unwrap();
19 | }))
20 | .run(tauri::generate_context!())
21 | .expect("error while running tauri application");
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/src/api/Todos/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 |
3 | export interface AddTodo {
4 | projectTodoID: number;
5 | title: string;
6 | description: string;
7 | }
8 |
9 | export interface ToggleTodo {
10 | todoID: number;
11 | isDone: boolean;
12 | }
13 |
14 | export const postAddTodo = async (todo: AddTodo) => {
15 | return await axiosInstance.post('/api/todos/addTodo', todo);
16 | };
17 |
18 | export const patchToggleTodo = async (todo: ToggleTodo) => {
19 | return await axiosInstance.patch(`/api/todos/toggleStatus/${todo.todoID}`, {
20 | isDone: todo.isDone,
21 | });
22 | };
23 |
24 | export const postDeleteTodo = async (todoID: number) => {
25 | return await axiosInstance.delete(`/api/todos/deleteTodo/${todoID}`);
26 | };
27 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Projects/ProjectByStatusDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.ProjectTasks;
2 | using backend.Enums;
3 |
4 | namespace backend.Dto.Projects
5 | {
6 | public class ProjectByStatusDto
7 | {
8 | public int ProjectID { get; set; }
9 | public required string Title { get; set; }
10 | public required string Description { get; set; }
11 | public DateTime Deadline { get; set; }
12 | public ProjectStatus Status { get; set; }
13 | public ICollection? Backlog { get; set; }
14 | public ICollection? inProgress { get; set; }
15 | public ICollection? Review { get; set; }
16 | public ICollection? Closed { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "Zenith"
3 | version = "0.0.1"
4 | description = "Zenith"
5 | authors = ["you"]
6 | edition = "2021"
7 |
8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9 |
10 | [build-dependencies]
11 | tauri-build = { version = "1.5", features = [] }
12 |
13 | [dependencies]
14 | tauri = { version = "1.5", features = ["shell-open"] }
15 | serde = { version = "1.0", features = ["derive"] }
16 | serde_json = "1.0"
17 | tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
18 |
19 | [features]
20 | # this feature is used for production builds or when `devPath` points to the filesystem
21 | # DO NOT REMOVE!!
22 | custom-protocol = ["tauri/custom-protocol"]
23 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 | "baseUrl": ".",
17 | "paths": {
18 | "~/*": ["src/*"],
19 | },
20 |
21 | /* Linting */
22 | "strict": true,
23 | "noUnusedLocals": true,
24 | "noUnusedParameters": true,
25 | "noFallthroughCasesInSwitch": true
26 | },
27 | "include": ["src"],
28 | "references": [{ "path": "./tsconfig.node.json" }]
29 | }
30 |
--------------------------------------------------------------------------------
/backend/backend/.env.example:
--------------------------------------------------------------------------------
1 | # local
2 | #CONNECTION_STRING="Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=zenith;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False"
3 |
4 | # docker
5 | CONNECTION_STRING="Server=mssql,1433;Database=zenith;User=sa;Password=Password1*;TrustServerCertificate=True;TrustServerCertificate=True;Trusted_Connection=False; MultipleActiveResultSets=true"
6 |
7 | EMAIL_SENDER="xxxx"
8 | EMAIL_PASSWORD="xxxx"
9 |
10 | JWT_KEY="PRIVATE_KEY_ZENITH_KEY_AUTHORIZATION_KEY"
11 |
12 | # local
13 | #JWT_ISSUER="https://localhost:7086/api"
14 |
15 | #docker
16 | JWT_ISSUER="http://localhost:5000/api"
17 |
18 | JWT_EPIRE_MINUTES="30"
19 |
20 | ASPNETCORE_ENVIRONMENT="Production"
21 | MSSQL_SA_PASSWORD="Password1*"
22 |
23 |
--------------------------------------------------------------------------------
/frontend/src/api/Settings/query.ts:
--------------------------------------------------------------------------------
1 | import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2 | import { useAuth } from '~/context/AuthContext';
3 | import { Settings, editSettings, querySettings } from './api';
4 |
5 | export const getSettings = () => {
6 | const { isAuthenticated } = useAuth();
7 | return useQuery({
8 | queryKey: ['allSettings'],
9 | queryFn: querySettings,
10 | enabled: isAuthenticated,
11 | retryDelay: 10
12 | });
13 | };
14 |
15 | export const mutateEditSettings = () => {
16 | const queryClient = useQueryClient();
17 | return useMutation({
18 | mutationKey: ['editSettings'],
19 | mutationFn: (preferences: Settings) => editSettings(preferences),
20 | onSuccess: () => {
21 | queryClient.invalidateQueries({ queryKey: ['allSettings'] });
22 | },
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/frontend/src/component/SubDrawer/SubDrawer.tsx:
--------------------------------------------------------------------------------
1 | import { Drawer, DrawerProps } from '@mui/material';
2 | import { SIDEBAR_WIDTH } from '~/config/constants';
3 |
4 | export const SubDrawer = ({ children, sx, ...rest }: DrawerProps) => {
5 | return (
6 |
27 | {children}
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/frontend/src/api/Notifications/query.ts:
--------------------------------------------------------------------------------
1 | import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2 | import { patchMarkAsRead, queryAllNotifications } from './api';
3 | import { enqueueSnackbar } from 'notistack';
4 | import { t } from '@lingui/macro';
5 |
6 | export const getAllNotifications = () => {
7 | return useQuery({
8 | queryKey: ['allNotifications'],
9 | queryFn: queryAllNotifications,
10 | });
11 | };
12 |
13 | export const mutateMarkAsRead = () => {
14 | const queryClient = useQueryClient();
15 | return useMutation({
16 | mutationKey: ['markAsRead'],
17 | mutationFn: (id:number) =>
18 | patchMarkAsRead(id),
19 | onSuccess: () => {
20 | enqueueSnackbar(t({message:'Notification dismissed'}));
21 | queryClient.invalidateQueries({ queryKey: ['allNotifications'] });
22 | },
23 | });
24 | };
--------------------------------------------------------------------------------
/backend/backend/Dto/GroupProjects/GroupProjectByStatusDto.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.GroupProjectTasks;
2 | using backend.Enums;
3 |
4 | namespace backend.Dto.GroupProjects
5 | {
6 | public class GroupProjectByStatusDto
7 | {
8 | public int ProjectID { get; set; }
9 | public required string Title { get; set; }
10 | public required string Description { get; set; }
11 | public DateTime Deadline { get; set; }
12 | public ProjectStatus Status { get; set; }
13 | public string? User { get; set; }
14 | public ICollection? Backlog { get; set; }
15 | public ICollection? inProgress { get; set; }
16 | public ICollection? Review { get; set; }
17 | public ICollection? Closed { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/View/NotesView/NoNoteView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography } from '@mui/material';
2 | import DescriptionIcon from '@mui/icons-material/Description';
3 | import { Trans } from '@lingui/macro';
4 |
5 | export const NoNoteView = () => {
6 | return (
7 |
19 |
20 |
21 | No Note
22 |
23 |
24 | Create a new note to see preview
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/backend/backend/Migrations/20240616124753_avatar_images.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace backend.Migrations
6 | {
7 | ///
8 | public partial class Avatar_images : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AddColumn(
14 | name: "Image",
15 | table: "Users",
16 | type: "nvarchar(max)",
17 | nullable: true);
18 | }
19 |
20 | ///
21 | protected override void Down(MigrationBuilder migrationBuilder)
22 | {
23 | migrationBuilder.DropColumn(
24 | name: "Image",
25 | table: "Users");
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/src/View/NotesView/DrawerLink.tsx:
--------------------------------------------------------------------------------
1 | import { ListItem, ListItemButton, ListItemText, alpha } from '@mui/material';
2 |
3 | type DrawerProps = {
4 | isActive: boolean;
5 | children: React.ReactNode;
6 | color?: string;
7 | };
8 |
9 | export const DrawerLink = ({ isActive, children, color }: DrawerProps) => {
10 | return (
11 |
12 | ({
14 | backgroundColor: isActive
15 | ? color
16 | ? alpha(color, theme.palette.action.activatedOpacity)
17 | : theme.palette.action.focus
18 | : '',
19 | display: 'flex',
20 | borderRadius: '10px',
21 | minHeight: '54px',
22 | })}
23 | >
24 | {children}
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:@typescript-eslint/recommended",
10 | "plugin:react/recommended"
11 | ],
12 | "parser": "@typescript-eslint/parser",
13 | "parserOptions": {
14 | "ecmaVersion": 12,
15 | "sourceType": "module",
16 | "ecmaFeatures": {
17 | "jsx": true
18 | }
19 | },
20 | "plugins": [
21 | "@typescript-eslint",
22 | "react"
23 | ],
24 | "rules": {
25 | "semi": "off",
26 | "@typescript-eslint/semi": ["warn", "never"],
27 | "react/prop-types": "off",
28 | "react/react-in-jsx-scope": "off"
29 | },
30 | "settings": {
31 | "react": {
32 | "version": "detect"
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/NoTodoView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography } from '@mui/material';
2 | import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
3 | import { Trans } from '@lingui/macro';
4 |
5 | export const NoTodoView = () => {
6 | return (
7 |
19 |
20 |
21 | No Todo
22 |
23 |
24 | Create a new todo to see preview
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/TokenController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Token;
2 | using backend.Dto.Users;
3 | using backend.Interface;
4 | using backend.Repository;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc;
7 | using System.Data;
8 |
9 | namespace backend.Controllers
10 | {
11 | [Route("api/token")]
12 | [ApiController]
13 | public class TokenController : ControllerBase
14 | {
15 | private readonly ITokenRepository _tokenService;
16 | public TokenController(ITokenRepository tokenService)
17 | {
18 | _tokenService = tokenService;
19 | }
20 | [HttpPost("refresh")]
21 | public async Task> RefreshToken([FromBody] AccessTokenDto dto)
22 | {
23 | var response = await _tokenService.Refresh(dto);
24 | return Ok(response);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/frontend/src/View/Errors/NotFoundView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, Typography } from '@mui/material';
2 | import { NavLink } from 'react-router-dom';
3 | import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
4 | import { Trans } from '@lingui/macro';
5 |
6 | export const NotFoundView = () => {
7 | return (
8 |
19 |
20 |
21 | View not found
22 |
23 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/frontend/src/component/Layout/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@mui/material';
2 | import { Outlet } from 'react-router-dom';
3 | import { Sidebar } from '../Sidebar/Sidebar';
4 | import { LoadingView } from '~/View/LoadingView/LoadingView';
5 | import { Suspense } from 'react';
6 |
7 | type RouterProps = {
8 | routes: { [routeName: string]: boolean };
9 | };
10 |
11 | export const Layout = ({ routes }: RouterProps) => {
12 | return (
13 |
14 |
15 |
25 | }>
26 |
27 |
28 |
29 |
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { lingui } from "@lingui/vite-plugin";
2 | import react from '@vitejs/plugin-react';
3 | import { resolve } from 'path';
4 | import { defineConfig, loadEnv } from 'vite';
5 |
6 | export default defineConfig(({ mode }) => {
7 | const env = loadEnv(mode, process.cwd());
8 |
9 | return {
10 | define: {
11 | 'process.env': process.env,
12 | },
13 | plugins: [
14 | react({
15 | babel: {
16 | plugins: ["macros"],
17 | },
18 | }),
19 | lingui(),
20 | ],
21 | resolve: {
22 | alias: {
23 | '~': resolve(__dirname, 'src'),
24 | },
25 | },
26 | clearScreen: false,
27 | server: {
28 | port: 1420,
29 | strictPort: true,
30 | proxy: {
31 | '/api': {
32 | target: env.VITE_API_URL,
33 | changeOrigin: true,
34 | secure: false,
35 | },
36 | },
37 | watch: {
38 | ignored: ['**/src-tauri/**'],
39 | },
40 | },
41 | };
42 | });
43 |
--------------------------------------------------------------------------------
/frontend/src/View/NoConnectionView/NoConnectionView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography, Button } from '@mui/material';
2 | import { NavLink } from 'react-router-dom';
3 | import CloudOffIcon from '@mui/icons-material/CloudOff';
4 | import { Trans } from '@lingui/macro';
5 |
6 | export const NoConnectionView = () => {
7 | return (
8 |
19 |
20 |
21 | Connection not established
22 |
23 |
26 |
29 |
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/backend/backend/Repository/UserContextRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Interface;
2 | using System.Security.Claims;
3 |
4 | namespace backend.Repository
5 | {
6 |
7 | public class UserContextRepository : IUserContextRepository
8 | {
9 | private readonly IHttpContextAccessor _httpContextAccessor;
10 | public UserContextRepository(IHttpContextAccessor httpContextAccessor)
11 | {
12 | _httpContextAccessor = httpContextAccessor;
13 | }
14 | public ClaimsPrincipal User =>
15 | _httpContextAccessor.HttpContext?.User ?? new ClaimsPrincipal();
16 | public int? GetUserId
17 | {
18 | get
19 | {
20 | var user = User;
21 | var claim = user.FindFirst(x => x.Type == ClaimTypes.NameIdentifier);
22 | if (claim == null || !int.TryParse(claim.Value, out int userId))
23 | {
24 | return null;
25 | }
26 | return userId;
27 | }
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/frontend/src/api/Image/query.ts:
--------------------------------------------------------------------------------
1 | import { useMutation } from '@tanstack/react-query';
2 | import { deleteImage, postImage } from './api';
3 | import { useSnackbar } from 'notistack';
4 | import { t } from '@lingui/macro';
5 | import { queryClient } from '../api';
6 |
7 | export const mutatePostImage = () => {
8 | const { enqueueSnackbar } = useSnackbar();
9 | return useMutation({
10 | mutationKey: ['postImage'],
11 | mutationFn: (data: FormData) => postImage(data),
12 | onSuccess: () => {
13 | enqueueSnackbar(t({ message: 'Avatar updated' }));
14 | queryClient.invalidateQueries({ queryKey: ['getMyAccount'] });
15 | },
16 | });
17 | };
18 |
19 | export const mutateDeleteImage = () => {
20 | const { enqueueSnackbar } = useSnackbar();
21 | return useMutation({
22 | mutationKey: ['deleteImage'],
23 | mutationFn: (data: string) => deleteImage(data),
24 | onSuccess: () => {
25 | enqueueSnackbar(t({ message: 'Avatar deleted' }));
26 | queryClient.invalidateQueries({ queryKey: ['getMyAccount'] });
27 | },
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/frontend/src/component/InfiniteScroll/InfiniteScroll.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useCallback, ReactNode } from 'react';
2 |
3 | interface InfiniteScrollProps {
4 | loadMore: () => void;
5 | hasMore: boolean;
6 | isLoading: boolean;
7 | children: ReactNode;
8 | }
9 |
10 | export const InfiniteScroll: React.FC = ({
11 | loadMore,
12 | hasMore,
13 | isLoading,
14 | children,
15 | }) => {
16 | const observer = useRef(null);
17 |
18 | const lastElementRef = useCallback(
19 | (node: HTMLElement | null) => {
20 | if (isLoading) return;
21 | if (observer.current) observer.current.disconnect();
22 | observer.current = new IntersectionObserver((entries) => {
23 | if (entries[0].isIntersecting && hasMore) {
24 | loadMore();
25 | }
26 | });
27 | if (node) observer.current.observe(node);
28 | },
29 | [isLoading, hasMore, loadMore],
30 | );
31 |
32 | return (
33 |
34 | {children}
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/frontend/src/context/PdfContext.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | createContext,
3 | useState,
4 | ReactNode,
5 | Dispatch,
6 | SetStateAction,
7 | } from 'react';
8 |
9 | interface PdfContextProps {
10 | htmlContent: string;
11 | setHtmlContent: Dispatch>;
12 | selectedNote: number | null;
13 | setSelectedNote: Dispatch>;
14 | }
15 |
16 | export const PdfContext = createContext({
17 | htmlContent: '',
18 | setHtmlContent: () => {},
19 | selectedNote: null,
20 | setSelectedNote: () => {},
21 | });
22 |
23 | interface PdfProviderProps {
24 | children: ReactNode;
25 | }
26 |
27 | export const PdfProvider: React.FC = ({ children }) => {
28 | const [htmlContent, setHtmlContent] = useState('');
29 | const [selectedNote, setSelectedNote] = useState(null);
30 |
31 | return (
32 |
35 | {children}
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/frontend/src/component/PasswordField/PasswordField.tsx:
--------------------------------------------------------------------------------
1 | import { Visibility, VisibilityOff } from '@mui/icons-material';
2 | import {
3 | IconButton,
4 | InputAdornment,
5 | TextField,
6 | TextFieldProps,
7 | } from '@mui/material';
8 | import { useState } from 'react';
9 |
10 | type PasswordFieldProps = Omit;
11 |
12 | export const PasswordField = (props: PasswordFieldProps) => {
13 | const [showPassword, setShowPassword] = useState(false);
14 |
15 | const handleTogglePasswordVisibility = () => {
16 | setShowPassword((prev) => !prev);
17 | };
18 |
19 | return (
20 |
27 |
28 | {showPassword ? : }
29 |
30 |
31 | ),
32 | }}
33 | />
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/frontend/src-tauri/tauri.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "beforeDevCommand": "pnpm dev",
4 | "beforeBuildCommand": "pnpm build",
5 | "devPath": "http://localhost:1420",
6 | "distDir": "../dist"
7 | },
8 | "package": {
9 | "productName": "Zenith",
10 | "version": "0.0.1"
11 | },
12 | "tauri": {
13 | "allowlist": {
14 | "all": false,
15 | "shell": {
16 | "all": false,
17 | "open": true
18 | }
19 | },
20 | "windows": [
21 | {
22 | "fullscreen": false,
23 | "resizable": true,
24 | "title": "Zenith",
25 | "label": "main",
26 | "width": 1150,
27 | "height": 750,
28 | "fileDropEnabled": false
29 | }
30 | ],
31 | "security": {
32 | "csp": null
33 | },
34 | "bundle": {
35 | "active": true,
36 | "targets": "all",
37 | "identifier": "Zenith",
38 | "icon": [
39 | "icons/32x32.png",
40 | "icons/128x128.png",
41 | "icons/128x128@2x.png",
42 | "icons/icon.icns",
43 | "icons/icon.ico"
44 | ]
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/backend/backend/Models/Notification.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Models
2 | {
3 | public class Notification
4 | {
5 | public int NotificationID { get; set; }
6 | public int UserID { get; set; }
7 | public required string Message { get; set; }
8 | public DateTime DateTime { get; set; }
9 | public bool isActive { get; set; } = false;
10 | public bool isRead { get; set; } = false;
11 | public string? Discriminator { get; set; }
12 |
13 | public virtual User? User { get; set; }
14 |
15 | }
16 |
17 | public class GroupProjectNotification : Notification
18 | {
19 | public int GroupProjectID { get; set; }
20 | public GroupProject? GroupProject { get; set; }
21 | }
22 |
23 | public class ProjectNotification : Notification
24 | {
25 | public int ProjectID { get; set; }
26 | public Project? Project { get; set; }
27 | }
28 |
29 | public class CalendarEventNotification : Notification
30 | {
31 | public int CalendarEventID { get; set; }
32 | public CalendarEvent? CalendarEvent { get; set; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/src/component/SearchField/SearchField.tsx:
--------------------------------------------------------------------------------
1 | import { Search } from '@mui/icons-material';
2 | import {
3 | TextField,
4 | alpha,
5 | InputAdornment,
6 | TextFieldProps,
7 | } from '@mui/material';
8 | import { forwardRef } from 'react';
9 |
10 | export const SearchField = forwardRef(
11 | ({ InputProps, ...rest }, ref) => {
12 | return (
13 | ({
18 | '.MuiInputBase-root': {
19 | color: 'inherit',
20 | backgroundColor: alpha(theme.palette.common.white, 0.15),
21 | boxShadow: 'none',
22 | },
23 | svg: {
24 | color: theme.palette.action.disabled,
25 | },
26 | }),
27 | ]}
28 | size="small"
29 | autoComplete="off"
30 | InputProps={{
31 | ...InputProps,
32 | startAdornment: (
33 |
34 |
35 |
36 | ),
37 | }}
38 | />
39 | );
40 | },
41 | );
42 |
--------------------------------------------------------------------------------
/backend/backend.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.8.34408.163
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "backend", "backend\backend.csproj", "{791D8978-4456-4E87-9DCB-E337992D2E0D}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {791D8978-4456-4E87-9DCB-E337992D2E0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {791D8978-4456-4E87-9DCB-E337992D2E0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {791D8978-4456-4E87-9DCB-E337992D2E0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {791D8978-4456-4E87-9DCB-E337992D2E0D}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {9FA4EBF3-BCFF-430F-89A0-FE1AFA1F256D}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/SettingsController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.UserPreferences;
2 | using backend.Interface;
3 | using backend.Repository;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace backend.Controllers
8 | {
9 | [Route("api/settings")]
10 | [ApiController]
11 | public class SettingsController : ControllerBase
12 | {
13 | private readonly IUserPreferencesRepository _projectService;
14 | public SettingsController(IUserPreferencesRepository projectService)
15 | {
16 | _projectService = projectService;
17 | }
18 |
19 |
20 | [HttpGet("getSettings")]
21 | [Authorize]
22 | public async Task> getSettings()
23 | {
24 | var project = await _projectService.GetSettings();
25 | return Ok(project);
26 | }
27 |
28 | [HttpPatch("updateSettings")]
29 | [Authorize]
30 | public async Task updateSettings(UserPreferencesDto dto)
31 | {
32 | await _projectService.UpdateUserPreferences(dto);
33 | return Ok();
34 | }
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/Section/LanguageSection.tsx:
--------------------------------------------------------------------------------
1 | import { Trans } from '@lingui/macro';
2 | import {
3 | Box,
4 | ToggleButton,
5 | ToggleButtonGroup,
6 | Typography,
7 | } from '@mui/material';
8 | import { Settings } from '~/api/Settings/api';
9 | import { newSettingsProps } from '../SettingsView';
10 |
11 | type Props = {
12 | handleClick: (newSettings: newSettingsProps) => void;
13 | settings: Settings | undefined;
14 | };
15 |
16 | export const LanguageSection = ({ settings, handleClick }: Props) => {
17 | return (
18 |
19 |
20 | Language:
21 |
22 |
23 | handleClick({ language: 'en' })}
26 | >
27 | English
28 |
29 | handleClick({ language: 'pl' })}
32 | >
33 | Polish
34 |
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/frontend/src/View/Errors/ErrorView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, Typography } from '@mui/material';
2 | import { useRouteError } from 'react-router-dom';
3 | import BlockIcon from '@mui/icons-material/Block';
4 | import { Trans } from '@lingui/macro';
5 |
6 | const handleError = (error: Error | string) => {
7 | if (error instanceof Error) {
8 | return error.message;
9 | }
10 |
11 | return error;
12 | };
13 |
14 | export const ErrorView = () => {
15 | const error = useRouteError() as string;
16 |
17 | const refresh = () => location.reload();
18 |
19 | return (
20 |
31 |
32 |
33 | Unexpected Error:
34 |
35 | {handleError(error)}
36 |
39 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/frontend/src/View/RegisterView/PasswordRenew/EmailSection.tsx:
--------------------------------------------------------------------------------
1 | import { UseFormReturn, useController } from 'react-hook-form';
2 | import { forgotPasswordModel } from './schema';
3 | import { Box, TextField } from '@mui/material';
4 | import { Trans } from '@lingui/macro';
5 |
6 | type Props = {
7 | onSubmit: (value: forgotPasswordModel) => void;
8 | formContext: UseFormReturn;
9 | };
10 |
11 | export const EmailSection = ({ onSubmit, formContext }: Props) => {
12 | const {
13 | control,
14 | handleSubmit,
15 | formState: { errors },
16 | } = formContext;
17 |
18 | const email = useController({
19 | control: control,
20 | name: 'email',
21 | });
22 |
23 | return (
24 | {
28 | onSubmit(data);
29 | })}
30 | >
31 | Email}
33 | ref={email.field.ref}
34 | onChange={email.field.onChange}
35 | onBlur={email.field.onBlur}
36 | name={email.field.name}
37 | error={errors.email !== undefined}
38 | helperText={errors.email?.message}
39 | />
40 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/backend/backend/Repository/EmailRepository.cs:
--------------------------------------------------------------------------------
1 | using backend.Interface;
2 | using backend;
3 | using System.Net.Mail;
4 | using System.Net;
5 |
6 | public class EmailRepository : IEmailRepository
7 | {
8 | private readonly EmailSettings _settings;
9 |
10 | public EmailRepository(EmailSettings settings)
11 | {
12 | _settings = settings;
13 | }
14 |
15 | public async Task SendEmailAsync(string email, string subject, string message)
16 | {
17 | if (string.IsNullOrEmpty(_settings.Email))
18 | {
19 | throw new ArgumentException("Service email not found");
20 | }
21 |
22 | var mailMessage = new MailMessage();
23 | mailMessage.From = new MailAddress(_settings.Email);
24 | mailMessage.Subject = subject;
25 | mailMessage.To.Add(new MailAddress(email));
26 | mailMessage.Body = message;
27 | mailMessage.IsBodyHtml = true;
28 |
29 | var client = new SmtpClient("smtp.gmail.com")
30 | {
31 | Port = 587,
32 | EnableSsl = true,
33 | Credentials = new NetworkCredential(_settings.Email, _settings.Password)
34 | };
35 |
36 |
37 |
38 | await client.SendMailAsync(mailMessage);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/backend/backend/Migrations/20240708220343_refresh-token.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | #nullable disable
5 |
6 | namespace backend.Migrations
7 | {
8 | ///
9 | public partial class Refreshtoken : Migration
10 | {
11 | ///
12 | protected override void Up(MigrationBuilder migrationBuilder)
13 | {
14 | migrationBuilder.AddColumn(
15 | name: "RefreshToken",
16 | table: "Users",
17 | type: "nvarchar(max)",
18 | nullable: true);
19 |
20 | migrationBuilder.AddColumn(
21 | name: "RefreshTokenExpiry",
22 | table: "Users",
23 | type: "datetime2",
24 | nullable: true);
25 | }
26 |
27 | ///
28 | protected override void Down(MigrationBuilder migrationBuilder)
29 | {
30 | migrationBuilder.DropColumn(
31 | name: "RefreshToken",
32 | table: "Users");
33 |
34 | migrationBuilder.DropColumn(
35 | name: "RefreshTokenExpiry",
36 | table: "Users");
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/NotificationController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Notifications;
2 | using backend.Interface;
3 | using backend.Models;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace backend.Controllers
8 | {
9 | [Route("api/notifications")]
10 | [ApiController]
11 | public class NotificationController : ControllerBase
12 | {
13 | private readonly INotificationRepository _notificationService;
14 | public NotificationController(INotificationRepository notificationService)
15 | {
16 | _notificationService = notificationService;
17 | }
18 |
19 | [HttpGet("get")]
20 | [Authorize]
21 | public async Task> GetAllNotes()
22 | {
23 | var notifications = await _notificationService.GetNotifications();
24 | return Ok(notifications);
25 | }
26 |
27 | [HttpPatch("markAsRead/{notificationId}")]
28 | [Authorize]
29 | public async Task> MarkAsRead([FromRoute] int notificationId)
30 | {
31 | await _notificationService.MarkAsRead(notificationId);
32 | return Ok();
33 | }
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/frontend/src/component/UserBox/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const userSchema = z
4 | .object({
5 | username: z.string().min(1, { message: 'Username is required' }),
6 | email: z.string().min(1, { message: 'Email is required' }).email({ message: 'Invalid email address' }),
7 | oldPassword: z.string().min(8, { message: 'Old password must be at least 8 characters' }).optional().or(z.literal('')),
8 | password: z.string().min(8, { message: 'Password must be at least 8 characters' }).optional().or(z.literal('')),
9 | passwordConfirm: z.string().optional(),
10 | })
11 | .refine((data) => {
12 | if (data.oldPassword && data.oldPassword !== '') {
13 | return data.password && data.passwordConfirm;
14 | }
15 | return true;
16 | }, {
17 | message: 'New password is required',
18 | path: ['password'],
19 | })
20 | .refine((data) => data.password === data.passwordConfirm, {
21 | message: "Passwords don't match",
22 | path: ['passwordConfirm'],
23 | });
24 |
25 | export type userModel = z.infer;
26 |
27 |
28 |
29 | export const avatarSchema = z
30 | .object({
31 | image: z.union([z.instanceof(File), z.null()]),
32 | })
33 |
34 | export type avatarModel = z.infer;
35 |
--------------------------------------------------------------------------------
/backend/backend/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:58289",
8 | "sslPort": 44337
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "applicationUrl": "http://localhost:5246",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "launchUrl": "swagger",
27 | "applicationUrl": "https://localhost:7086;http://localhost:5246",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "launchUrl": "swagger",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectView/UpdateGroup/CreateForm.tsx:
--------------------------------------------------------------------------------
1 | import { Box, TextField } from '@mui/material';
2 | import { groupModel } from './schema';
3 | import { UseFormReturn, useController } from 'react-hook-form';
4 | import { Trans } from '@lingui/macro';
5 |
6 | type Props = {
7 | onSubmit: (value: groupModel) => void;
8 | formContext: UseFormReturn;
9 | };
10 |
11 | export const CreateForm = ({ onSubmit, formContext }: Props) => {
12 | const {
13 | control,
14 | handleSubmit,
15 | formState: { errors },
16 | } = formContext;
17 |
18 | const groupName = useController({
19 | control: control,
20 | name: 'groupName',
21 | });
22 |
23 | return (
24 |
30 | Group name}
32 | ref={groupName.field.ref}
33 | value={groupName.field.value}
34 | onChange={groupName.field.onChange}
35 | onBlur={groupName.field.onBlur}
36 | name={groupName.field.name}
37 | error={errors.groupName !== undefined}
38 | helperText={errors.groupName?.message}
39 | />
40 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/frontend/src/context/GroupRole.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ReactNode,
3 | createContext,
4 | useContext,
5 | useEffect,
6 | useState,
7 | } from 'react';
8 | import { GroupRole } from '~/api/Group/api';
9 | import { isAdmin } from '~/utils/useGroupRoles';
10 |
11 | interface IGroupContext {
12 | userRole: GroupRole;
13 | isGranted: boolean;
14 | isModerator: boolean;
15 | setUserRole: (role: GroupRole) => void;
16 | }
17 |
18 | const GroupContext = createContext({
19 | userRole: GroupRole.User,
20 | isGranted: false,
21 | isModerator: false,
22 | setUserRole: () => {},
23 | });
24 |
25 | export const useGroupContext = () => useContext(GroupContext);
26 |
27 | type Props = { children: ReactNode };
28 |
29 | export const GroupProvider = ({ children }: Props) => {
30 | const [userRole, setUserRole] = useState(GroupRole.User);
31 | const [isGranted, setIsGranted] = useState(false);
32 | const [isModerator, setIsModerator] = useState(false);
33 |
34 | useEffect(() => {
35 | setIsGranted(isAdmin(userRole));
36 | setIsModerator(userRole >= 1 ? true : false);
37 | }, [userRole]);
38 |
39 | return (
40 |
43 | {children}
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/frontend/src/api/Dashboard/query.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@tanstack/react-query';
2 | import {
3 | queryEventsDashboard,
4 | queryGroupProjectsDashboard,
5 | queryNotesDashboard,
6 | queryProjectsDashboard,
7 | queryTodoDashboard,
8 | } from './api';
9 |
10 | export const getTodoDashboard = () => {
11 | return useQuery({
12 | queryKey: ['todoDashboard'],
13 | queryFn: queryTodoDashboard,
14 | });
15 | };
16 |
17 | export const getEventsDashboard = (maxEvents: number = 5) => {
18 | return useQuery({
19 | queryKey: ['eventsDashboard', maxEvents],
20 | queryFn: () => queryEventsDashboard(maxEvents),
21 | });
22 | };
23 |
24 | export const getNotesDashboard = (maxNotes: number = 5) => {
25 | return useQuery({
26 | queryKey: ['notesDashboard', maxNotes],
27 | queryFn: () => queryNotesDashboard(maxNotes),
28 | });
29 | };
30 |
31 | export const getProjectsDashboard = (daysThreshold: number = 7) => {
32 | return useQuery({
33 | queryKey: ['projectsDashboard', daysThreshold],
34 | queryFn: () => queryProjectsDashboard(daysThreshold),
35 | });
36 | };
37 |
38 | export const getGroupProjectsDashboard = (daysThreshold: number = 7) => {
39 | return useQuery({
40 | queryKey: ['GroupProjectsDashboard', daysThreshold],
41 | queryFn: () => queryGroupProjectsDashboard(daysThreshold),
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/Section/RouteSection.tsx:
--------------------------------------------------------------------------------
1 | import { Trans } from '@lingui/macro';
2 | import { Box, Checkbox, Typography } from '@mui/material';
3 | import { Settings } from '~/api/Settings/api';
4 | import { newSettingsProps } from '../SettingsView';
5 |
6 | type CheckboxProps = {
7 | name: string;
8 | checked: boolean;
9 | onClick: () => void;
10 | };
11 |
12 | const CheckboxOption = ({ name, checked, onClick }: CheckboxProps) => (
13 |
14 |
15 | {name}
16 |
17 | );
18 |
19 | type Props = {
20 | handleClick: (newSettings: newSettingsProps) => void;
21 | settings: Settings | undefined;
22 | };
23 |
24 | export const RouteSection = ({ settings, handleClick }: Props) => {
25 | return (
26 | <>
27 |
28 | Routes:
29 |
30 | {Object.entries(settings!.routes).map(([routeName, isChecked]) => (
31 |
36 | handleClick({
37 | routes: { ...settings!.routes, [routeName]: !isChecked },
38 | })
39 | }
40 | />
41 | ))}
42 | >
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/frontend/src/View/HomeView/HomeView.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography } from '@mui/material';
2 | import { getMyAccount } from '~/api/User/query';
3 | import { Clock } from '~/component/Clock';
4 | import { NotificationBox } from '~/component/NotificationBox';
5 | import { UserBox } from '~/component/UserBox';
6 | import { LoadingView } from '../LoadingView/LoadingView';
7 |
8 | export const HomeView = () => {
9 | const today = new Date().toLocaleDateString();
10 | const { data: user, isLoading } = getMyAccount();
11 |
12 | if (isLoading || user === undefined) {
13 | return ;
14 | }
15 |
16 | return (
17 |
25 |
31 |
32 |
33 | {today}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/frontend/src/View/NotesView/NotesView.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@mui/material';
2 | import { Main } from '~/component/Main';
3 | import { useContext, useRef, useState } from 'react';
4 | import { debounce } from 'lodash';
5 | import { NotePreview } from './NotePreview';
6 | import { NoNoteView } from './NoNoteView';
7 | import { NotesDrawer } from './NotesDrawer';
8 | import { PdfContext } from '~/context/PdfContext';
9 |
10 | export const NotesView = () => {
11 | const { selectedNote, setSelectedNote } = useContext(PdfContext);
12 | const pageNumber = 1;
13 | const [filter, setFilter] = useState('');
14 | const pageSize = 5;
15 |
16 | const handleFilter = useRef(
17 | debounce(
18 | (event: React.ChangeEvent) =>
19 | setFilter(event.target.value),
20 | 500,
21 | ),
22 | ).current;
23 |
24 | return (
25 |
26 |
33 |
34 | {selectedNote ? (
35 |
36 | ) : (
37 |
38 | )}
39 |
40 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/frontend/src/View/CalendarView/MonthView/EventChip.tsx:
--------------------------------------------------------------------------------
1 | import { Paper, Typography, alpha } from '@mui/material';
2 | import { DialogEdit } from '../DialogEdit';
3 | import { useState } from 'react';
4 | import { CalendarEvent } from '~/api/Calendar/api';
5 |
6 | type Props = {
7 | event: CalendarEvent;
8 | };
9 |
10 | export const EventChip = ({ event }: Props) => {
11 | const [openEdit, setOpenEdit] = useState(false);
12 |
13 | const handleClickOpenEdit = () => {
14 | setOpenEdit(true);
15 | };
16 |
17 | return (
18 | <>
19 | ({
21 | backgroundColor: alpha(
22 | event.eventColor,
23 | theme.palette.action.disabledOpacity,
24 | ),
25 | borderLeft: '5px solid',
26 | borderColor: event.eventColor,
27 | padding: 0.5,
28 | cursor: 'pointer',
29 | })}
30 | onClick={handleClickOpenEdit}
31 | >
32 |
39 | {event.title}
40 |
41 |
42 |
48 | >
49 | );
50 | };
51 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/Section/ColorSection.tsx:
--------------------------------------------------------------------------------
1 | import CheckIcon from '@mui/icons-material/Check';
2 | import { Box, ClassNameMap, Paper, Tooltip } from '@mui/material';
3 | import { Settings } from '~/api/Settings/api';
4 | import { newSettingsProps } from '../SettingsView';
5 |
6 | type Props = {
7 | name: string;
8 | value: string;
9 | color: string;
10 | classes: ClassNameMap<'paper' | 'box'>;
11 | handleClick: (newSettings: newSettingsProps) => void;
12 | settings: Settings | undefined;
13 | };
14 |
15 | export const ColorSection = ({
16 | name,
17 | value,
18 | color,
19 | classes,
20 | settings,
21 | handleClick,
22 | }: Props) => {
23 | return (
24 |
25 |
26 | handleClick({ color: value })}
32 | >
33 | {settings?.color === value && (
34 | ({
36 | color: theme.palette.getContrastText(color),
37 | stroke: theme.palette.getContrastText(color),
38 | strokeWidth: 2,
39 | })}
40 | />
41 | )}
42 |
43 |
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/backend/backend/Models/User.cs:
--------------------------------------------------------------------------------
1 | using backend.Enums;
2 | using System.ComponentModel.DataAnnotations;
3 |
4 | namespace backend.Models
5 | {
6 | public class User
7 | {
8 | public int UserID { get; set; }
9 | public string? RefreshToken { get; set; }
10 | public DateTime? RefreshTokenExpiry { get; set; }
11 | public string? VerificationToken { get; set; }
12 | public string? PasswordResetToken { get; set; }
13 | public DateTime? PasswordResetTime { get; set; }
14 | public required string Username { get; set; }
15 | public required string Email { get; set; }
16 | public required string Password { get; set; }
17 | public Roles Role { get; set; }
18 | public int? GroupID { get; set; }
19 | public string? Image { get; set; }
20 |
21 | public virtual UserPreferences? Preferences { get; set; }
22 | public virtual Group? Group { get; set; }
23 | public virtual ICollection? Projects { get; set; }
24 | public virtual ICollection? Notes { get; set; }
25 | public virtual ICollection? CalendarEvents { get; set; }
26 | public virtual ICollection? Notifications { get; set; }
27 | public virtual ICollection? KanbanTasks { get; set; }
28 | public virtual ICollection? ProjectTodos { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/frontend/src/View/DashboardView/Sections/TodoSection.tsx:
--------------------------------------------------------------------------------
1 | import { Trans } from '@lingui/macro';
2 | import { Box, Paper, Typography } from '@mui/material';
3 | import { Gauge } from '@mui/x-charts/Gauge';
4 | import { getTodoDashboard } from '~/api/Dashboard/query';
5 | import { LoadingView } from '~/View/LoadingView/LoadingView';
6 |
7 | export const TodoSection = () => {
8 | const { data, isLoading } = getTodoDashboard();
9 |
10 | if (!data || isLoading) return ;
11 |
12 | return (
13 |
24 |
25 | Todo projects
26 |
27 |
33 |
39 |
40 | {data.totalTodos} Total toods
41 |
42 |
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/backend/dockerfile:
--------------------------------------------------------------------------------
1 | # See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
2 |
3 | # This stage is used when running from VS in fast mode (Default for Debug configuration)
4 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
5 | USER app
6 | WORKDIR /app
7 | EXPOSE 8080
8 | EXPOSE 8081
9 |
10 | # Install the necessary library for SQL Server connections
11 | USER root
12 | #RUN apt-get update && apt-get install -y libgssapi-krb5-2
13 |
14 | # This stage is used to build the service project
15 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
16 | ARG BUILD_CONFIGURATION=Release
17 | WORKDIR /src
18 | COPY ["backend/backend.csproj", "backend/"]
19 | RUN dotnet restore "./backend/backend.csproj"
20 | COPY . .
21 | WORKDIR "/src/backend"
22 | RUN dotnet build "./backend.csproj" -c $BUILD_CONFIGURATION -o /app/build
23 |
24 | # This stage is used to publish the service project to be copied to the final stage
25 | FROM build AS publish
26 | ARG BUILD_CONFIGURATION=Release
27 | RUN dotnet publish "./backend.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
28 |
29 | # This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
30 | FROM base AS final
31 | WORKDIR /app
32 | COPY --from=publish /app/publish .
33 | ENTRYPOINT ["dotnet", "backend.dll"]
--------------------------------------------------------------------------------
/frontend/src/api/Notifications/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 |
3 | interface GroupProjectNotification {
4 | notificationID: number;
5 | message: string;
6 | dateTime: Date;
7 | isActive: boolean;
8 | isRead: boolean;
9 | groupProjectID: number;
10 | }
11 |
12 | interface ProjectNotification {
13 | notificationID: number;
14 | message: string;
15 | dateTime: Date;
16 | isActive: boolean;
17 | isRead: boolean;
18 | projectID: number;
19 | }
20 |
21 | interface CalendarEventNotification {
22 | notificationID: number;
23 | message: string;
24 | dateTime: Date;
25 | isActive: boolean;
26 | isRead: boolean;
27 | calendarEventID: number;
28 | }
29 |
30 | export type NotificationRow = {
31 | notificationID: number;
32 | message: string;
33 | dateTime: Date;
34 | isActive: boolean;
35 | isRead: boolean;
36 | }
37 |
38 | interface Notifications {
39 | groupProjectNotifications: GroupProjectNotification[];
40 | projectNotifications: ProjectNotification[];
41 | calendarEventNotifications: CalendarEventNotification[];
42 | }
43 |
44 |
45 | export const queryAllNotifications = async () => {
46 | const response = await axiosInstance.get('/api/notifications/get');
47 | return response.data as Notifications;
48 | };
49 |
50 |
51 | export const patchMarkAsRead = async (id: number) => {
52 | return await axiosInstance.patch(`/api/notifications/markAsRead/${id}`);
53 | };
54 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/ImageController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Images;
2 | using backend.Dto.Todos;
3 | using backend.Interface;
4 | using backend.Repository;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc;
7 |
8 | namespace backend.Controllers
9 | {
10 | [Route("api/images")]
11 | [ApiController]
12 | public class ImageController : ControllerBase
13 | {
14 | private readonly IImageRepository _imageService;
15 | public ImageController(IImageRepository imageService)
16 | {
17 | _imageService = imageService;
18 | }
19 |
20 | [HttpGet("{id}")]
21 | public IActionResult GetImage(string id)
22 | {
23 | var image = _imageService.Get(id);
24 | return File(image, "application/octet-stream", id);
25 | }
26 |
27 | [HttpPost]
28 | public async Task PostImage([FromForm] AddImageDto dto)
29 | {
30 | if (dto.Image == null)
31 | {
32 | return BadRequest("Invalid iamge");
33 | }
34 |
35 | await _imageService.SaveAsync(dto.Image);
36 | return Ok();
37 | }
38 | [HttpDelete("{id}")]
39 | public async Task DeleteImage(string id)
40 | {
41 | await _imageService.DeleteAvatar(id);
42 | return Ok();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/Section/ReminderSection.tsx:
--------------------------------------------------------------------------------
1 | import { Trans } from '@lingui/macro';
2 | import {
3 | Box,
4 | ToggleButton,
5 | ToggleButtonGroup,
6 | Typography,
7 | } from '@mui/material';
8 | import { Settings } from '~/api/Settings/api';
9 | import { newSettingsProps } from '../SettingsView';
10 |
11 | type Props = {
12 | handleClick: (newSettings: newSettingsProps) => void;
13 | settings: Settings | undefined;
14 | };
15 |
16 | export const ReminderSection = ({ settings, handleClick }: Props) => {
17 | return (
18 | <>
19 |
20 |
21 | Reminder:
22 |
23 |
24 | handleClick({ reminder: 3 })}>
25 | 3 Days
26 |
27 | handleClick({ reminder: 1 })}>
28 | 1 Day
29 |
30 | handleClick({ reminder: 0 })}>
31 | None
32 |
33 |
34 |
35 |
36 | Applied changes will be visible tomorrow
37 |
38 | >
39 | );
40 | };
41 |
--------------------------------------------------------------------------------
/frontend/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/View/SettingsView/Section/ThemeSection.tsx:
--------------------------------------------------------------------------------
1 | import CheckIcon from '@mui/icons-material/Check';
2 | import { Box, ClassNameMap, Paper, Tooltip } from '@mui/material';
3 | import { Settings } from '~/api/Settings/api';
4 | import { newSettingsProps } from '../SettingsView';
5 |
6 | type Props = {
7 | name: string;
8 | value: string;
9 | color: string;
10 | classes: ClassNameMap<'paper' | 'box'>;
11 | handleClick: (newSettings: newSettingsProps) => void;
12 | settings: Settings | undefined;
13 | };
14 |
15 | export const ThemeSection = ({
16 | name,
17 | value,
18 | color,
19 | classes,
20 | settings,
21 | handleClick,
22 | }: Props) => {
23 | return (
24 |
25 |
26 | handleClick({ theme: value })}
32 | >
33 | {settings?.theme === value && (
34 | ({
36 | color: theme.palette.getContrastText(color),
37 | stroke: theme.palette.getContrastText(
38 | theme.palette.background.default,
39 | ),
40 | strokeWidth: 2,
41 | })}
42 | />
43 | )}
44 |
45 |
46 |
47 | );
48 | };
49 |
--------------------------------------------------------------------------------
/frontend/src/api/Calendar/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 |
3 | export interface mutateEvent {
4 | title: string;
5 | description: string;
6 | dateTime: string;
7 | eventColor: string;
8 | }
9 |
10 | export interface EditEvent {
11 | eventId: number;
12 | event: mutateEvent;
13 | }
14 |
15 | export interface CalendarEvent {
16 | eventID: number;
17 | title: string;
18 | description: string;
19 | dateTime: string;
20 | eventColor: string;
21 | }
22 |
23 | export interface EventPagination {
24 | from: string;
25 | to: string;
26 | colors: string
27 | }
28 |
29 | export const queryEventBetween = async (pagination: EventPagination) => {
30 | const response = await axiosInstance.get(
31 | `/api/calendar/getEventBetween`,{
32 | params:{
33 | from: pagination.from,
34 | to: pagination.to,
35 | colors: pagination.colors
36 | }
37 | }
38 | );
39 | return response.data as CalendarEvent[];
40 | };
41 |
42 | export const postAddEvent = async (event: mutateEvent) => {
43 | return await axiosInstance.post('/api/calendar/addEvent', event);
44 | };
45 |
46 | export const patchEditEvent = async (event: EditEvent) => {
47 | return await axiosInstance.patch(
48 | `/api/calendar/updateEvent/${event.eventId}`,
49 | event.event,
50 | );
51 | };
52 |
53 | export const deleteEvent = async (eventId: number) => {
54 | return await axiosInstance.delete(`/api/calendar/deleteEvent/${eventId}`);
55 | };
56 |
--------------------------------------------------------------------------------
/frontend/src/context/CalendarContext.tsx:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs';
2 | import {
3 | Dispatch,
4 | ReactNode,
5 | createContext,
6 | useContext,
7 | useState,
8 | } from 'react';
9 |
10 | interface CalendarContextProps {
11 | monthAsNumber: number;
12 | setMonthAsNumber: Dispatch;
13 | weekAsNumber: number;
14 | setWeekAsNumber: Dispatch;
15 | colors: { [color: string]: string };
16 | }
17 |
18 | const CalendarContext = createContext(
19 | undefined,
20 | );
21 |
22 | type Props = { colors: { [color: string]: string }; children: ReactNode };
23 |
24 | export const CalendarProvider = ({ colors, children }: Props) => {
25 | const [monthAsNumber, setMonthAsNumber] = useState(dayjs().month());
26 | const [weekAsNumber, setWeekAsNumber] = useState(
27 | //TODO: validate if +1 always returns valid week or temporary fix
28 | dayjs().diff(dayjs().startOf('month'), 'week') + 1,
29 | );
30 |
31 | return (
32 |
41 | {children}
42 |
43 | );
44 | };
45 |
46 | export const useCalendar = () => {
47 | const context = useContext(CalendarContext);
48 | if (!context) {
49 | throw new Error('useCalendar must be used within its Provider');
50 | }
51 | return context;
52 | };
53 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Validators/RegisterUserDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using backend.Data;
2 | using backend.Dto.Users;
3 | using FluentValidation;
4 | using System;
5 |
6 | namespace backend.Dto.Validators
7 | {
8 | public class RegisterUserDtoValidator : AbstractValidator
9 | {
10 |
11 |
12 | public RegisterUserDtoValidator(DataContext dbContext)
13 | {
14 | RuleFor(x => x.Email)
15 | .EmailAddress()
16 | .NotEmpty();
17 | RuleFor(x => x.Password)
18 | .NotEmpty()
19 | .MinimumLength(8);
20 | RuleFor(x => x.Password)
21 | .Equal(y => y.PasswordConfirm);
22 | RuleFor(x => x.Username)
23 | .Custom((value, context) =>
24 | {
25 | var usernameInUse = dbContext.Users.Any(x => x.Username == value);
26 | if(usernameInUse)
27 | {
28 | context.AddFailure("Username", "This username is already taken");
29 | }
30 | });
31 | RuleFor(x => x.Email)
32 | .Custom((value, context) =>
33 | {
34 | var emailInUse = dbContext.Users.Any(x => x.Email == value);
35 | if (emailInUse)
36 | {
37 | context.AddFailure("Email", "This email is taken");
38 | }
39 | });
40 | }
41 |
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/TodoPreview.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography, alpha } from '@mui/material';
2 | import { LoadingView } from '../LoadingView/LoadingView';
3 | import { getProjectTodoById } from '~/api/ProjectTodos/query';
4 | import { DialogCreate } from './TodoTask/DialogCreate';
5 | import { TodoCard } from '~/component/TodoCard';
6 |
7 | type Props = {
8 | projectId: number;
9 | };
10 |
11 | export const TodoPreview = ({ projectId }: Props) => {
12 | const { data: project, isLoading } = getProjectTodoById(projectId);
13 |
14 | if (isLoading || project === undefined) {
15 | return ;
16 | }
17 |
18 | return (
19 |
28 |
34 | {project.title}
35 |
36 |
37 |
45 | {project.todos.map((todo) => (
46 |
47 | ))}
48 |
49 |
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/backend/backend/Dto/Notifications/NotificationDto.cs:
--------------------------------------------------------------------------------
1 | namespace backend.Dto.Notifications
2 | {
3 | public class NotificationDto
4 | {
5 | public List? GroupProjectNotifications { get; set; }
6 | public List? ProjectNotifications { get; set; }
7 | public List? CalendarEventNotifications { get; set; }
8 | }
9 |
10 | public class GroupProjectNotificationDto
11 | {
12 | public int NotificationID { get; set; }
13 | public required string Message { get; set; }
14 | public DateTime DateTime { get; set; }
15 | public bool isActive { get; set; }
16 | public bool isRead { get; set; }
17 | public int GroupProjectID { get; set; }
18 | }
19 |
20 | public class ProjectNotificationDto
21 | {
22 | public int NotificationID { get; set; }
23 | public required string Message { get; set; }
24 | public DateTime DateTime { get; set; }
25 | public bool isActive { get; set; }
26 | public bool isRead { get; set; }
27 | public int ProjectID { get; set; }
28 | }
29 |
30 | public class CalendarEventNotificationDto
31 | {
32 | public int NotificationID { get; set; }
33 | public required string Message { get; set; }
34 | public DateTime DateTime { get; set; }
35 | public bool isActive { get; set; }
36 | public bool isRead { get; set; }
37 | public int CalendarEventID { get; set; }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/context/AuthContext.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode, createContext, useContext, useState } from 'react';
2 | import { queryClient } from '~/api/api';
3 | import { AUTH_TOKEN, REFRESH_TOKEN } from '~/config/constants';
4 |
5 | interface AuthContextProps {
6 | isAuthenticated: boolean;
7 | login: (accessToken: string, refreshToken: string) => void;
8 | logout: () => void;
9 | }
10 |
11 | const AuthContext = createContext(undefined);
12 |
13 | type Props = { children: ReactNode };
14 |
15 | export const AuthProvider = ({ children }: Props) => {
16 | const [isAuthenticated, setIsAuthenticated] = useState(() => {
17 | return (
18 | localStorage.getItem(AUTH_TOKEN) !== null &&
19 | localStorage.getItem(REFRESH_TOKEN) !== null
20 | );
21 | });
22 |
23 | const login = (accessToken: string, refreshToken: string) => {
24 | localStorage.setItem(AUTH_TOKEN, accessToken);
25 | localStorage.setItem(REFRESH_TOKEN, refreshToken);
26 | setIsAuthenticated(true);
27 | };
28 |
29 | const logout = () => {
30 | localStorage.removeItem(AUTH_TOKEN);
31 | localStorage.removeItem(REFRESH_TOKEN);
32 | setIsAuthenticated(false);
33 | queryClient.removeQueries();
34 | };
35 |
36 | return (
37 |
38 | {children}
39 |
40 | );
41 | };
42 |
43 | export const useAuth = () => {
44 | const context = useContext(AuthContext);
45 | if (!context) {
46 | throw new Error('useAuth must be used within its Provider');
47 | }
48 | return context;
49 | };
50 |
--------------------------------------------------------------------------------
/frontend/src/api/Todos/query.ts:
--------------------------------------------------------------------------------
1 | import { useMutation, useQueryClient } from '@tanstack/react-query';
2 | import { useSnackbar } from 'notistack';
3 | import {
4 | AddTodo,
5 | ToggleTodo,
6 | patchToggleTodo,
7 | postAddTodo,
8 | postDeleteTodo,
9 | } from './api';
10 | import { t } from '@lingui/macro';
11 |
12 | export const mutateAddTodo = () => {
13 | const queryClient = useQueryClient();
14 | const { enqueueSnackbar } = useSnackbar();
15 | return useMutation({
16 | mutationKey: ['addTodo'],
17 | mutationFn: (todo: AddTodo) => postAddTodo(todo),
18 | onSuccess: () => {
19 | enqueueSnackbar(t({message:'Task added'}));
20 | queryClient.invalidateQueries({ queryKey: ['projectTodoById'] });
21 | },
22 | });
23 | };
24 |
25 | export const mutateToggleTodo = () => {
26 | const queryClient = useQueryClient();
27 | return useMutation({
28 | mutationKey: ['toggleTodo'],
29 | mutationFn: (todo: ToggleTodo) => patchToggleTodo(todo),
30 | onSuccess: () => {
31 | queryClient.invalidateQueries({ queryKey: ['projectTodoById'] });
32 | queryClient.invalidateQueries({ queryKey: ['projectTodos'] });
33 | },
34 | });
35 | };
36 |
37 | export const deleteTodo = () => {
38 | const queryClient = useQueryClient();
39 | const { enqueueSnackbar } = useSnackbar();
40 | return useMutation({
41 | mutationKey: ['deleteTodo'],
42 | mutationFn: (todoID: number) => postDeleteTodo(todoID),
43 | onSuccess: () => {
44 | enqueueSnackbar(t({message:'Task deleted'}));
45 | queryClient.invalidateQueries({ queryKey: ['projectTodoById'] });
46 | },
47 | });
48 | };
49 |
--------------------------------------------------------------------------------
/frontend/src/View/ProjectView/ProjectView.tsx:
--------------------------------------------------------------------------------
1 | import { AppBar, Box, Toolbar, Typography } from '@mui/material';
2 | import { DialogCreate } from './DialogCreate';
3 | import { SearchField } from '~/component/SearchField';
4 | import { Trans, t } from '@lingui/macro';
5 | import { useRef, useState } from 'react';
6 | import { debounce } from 'lodash';
7 | import { ProjectList } from './ProjectList';
8 |
9 | export const ProjectView = () => {
10 | const [pageNumber, setPageNumber] = useState(1);
11 | const [filter, setFilter] = useState('');
12 | const pageSize = 5;
13 |
14 | const onPageChange = (_: React.ChangeEvent, value: number) => {
15 | setPageNumber(value);
16 | };
17 |
18 | const handleFilter = useRef(
19 | debounce((event: React.ChangeEvent) => {
20 | setFilter(event.target.value);
21 | setPageNumber(1);
22 | }, 500),
23 | ).current;
24 |
25 | return (
26 |
27 |
28 |
29 |
30 | Projects
31 |
32 |
36 |
37 |
38 |
39 |
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/frontend/src/View/CalendarView/WeekView/WeekEventChip.tsx:
--------------------------------------------------------------------------------
1 | import { Paper, Typography, alpha } from '@mui/material';
2 | import { DialogEdit } from '../DialogEdit';
3 | import { useState } from 'react';
4 | import { CalendarEvent } from '~/api/Calendar/api';
5 |
6 | type Props = {
7 | event: CalendarEvent;
8 | };
9 |
10 | export const WeekEventChip = ({ event }: Props) => {
11 | const [openEdit, setOpenEdit] = useState(false);
12 |
13 | const handleClickOpenEdit = () => {
14 | setOpenEdit(true);
15 | };
16 |
17 | return (
18 | <>
19 | ({
21 | backgroundColor: alpha(
22 | event.eventColor,
23 | theme.palette.action.disabledOpacity,
24 | ),
25 | borderLeft: '5px solid',
26 | borderColor: event.eventColor,
27 | padding: 0.5,
28 | cursor: 'pointer',
29 | })}
30 | onClick={handleClickOpenEdit}
31 | >
32 |
39 | {event.title}
40 |
41 |
48 | {event.description}
49 |
50 |
51 |
57 | >
58 | );
59 | };
60 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectView/ProjectTab.tsx:
--------------------------------------------------------------------------------
1 | import { Trans, t } from '@lingui/macro';
2 | import { Box, Typography } from '@mui/material';
3 | import { debounce } from 'lodash';
4 | import { useRef, useState } from 'react';
5 | import { SearchField } from '~/component/SearchField';
6 | import { ProjectList } from './ProjectList';
7 |
8 | export const ProjectTab = () => {
9 | const [pageNumber, setPageNumber] = useState(1);
10 | const [filter, setFilter] = useState('');
11 | const pageSize = 5;
12 |
13 | const onPageChange = (_: React.ChangeEvent, value: number) => {
14 | setPageNumber(value);
15 | };
16 |
17 | const handleFilter = useRef(
18 | debounce((event: React.ChangeEvent) => {
19 | setFilter(event.target.value);
20 | setPageNumber(1);
21 | }, 500),
22 | ).current;
23 |
24 | return (
25 | <>
26 |
33 |
34 | Explore projects
35 |
36 |
37 |
41 |
42 |
43 |
48 | >
49 | );
50 | };
51 |
--------------------------------------------------------------------------------
/backend/backend/Migrations/20241106171010_calendar_colors.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace backend.Migrations
6 | {
7 | ///
8 | public partial class calendar_colors : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AlterColumn(
14 | name: "VerificationToken",
15 | table: "Users",
16 | type: "nvarchar(max)",
17 | nullable: true,
18 | oldClrType: typeof(string),
19 | oldType: "nvarchar(max)");
20 |
21 | migrationBuilder.AddColumn(
22 | name: "Colors",
23 | table: "UserPreferences",
24 | type: "nvarchar(max)",
25 | nullable: false,
26 | defaultValue: "");
27 | }
28 |
29 | ///
30 | protected override void Down(MigrationBuilder migrationBuilder)
31 | {
32 | migrationBuilder.DropColumn(
33 | name: "Colors",
34 | table: "UserPreferences");
35 |
36 | migrationBuilder.AlterColumn(
37 | name: "VerificationToken",
38 | table: "Users",
39 | type: "nvarchar(max)",
40 | nullable: false,
41 | defaultValue: "",
42 | oldClrType: typeof(string),
43 | oldType: "nvarchar(max)",
44 | oldNullable: true);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/frontend/src/Theme/Color.ts:
--------------------------------------------------------------------------------
1 | import { PaletteColorOptions } from '@mui/material';
2 |
3 | export const blue: PaletteColorOptions = {
4 | '50': '#E3F2FD',
5 | '100': '#BBDEFB',
6 | '200': '#90CAF9',
7 | '300': '#64B5F6',
8 | '400': '#42A5F5',
9 | '500': '#2196F3',
10 | '600': '#1E88E5',
11 | '700': '#1976D2',
12 | '800': '#1565C0',
13 | '900': '#0D47A1',
14 | A100: '#82B1FF',
15 | A200: '#448AFF',
16 | A400: '#2979FF',
17 | A700: '#2962FF',
18 | };
19 |
20 | export const purple: PaletteColorOptions = {
21 | '50': '#F8EBFA',
22 | '100': '#F1D4F3',
23 | '200': '#E4AAE8',
24 | '300': '#D680DD',
25 | '400': '#C966D2',
26 | '500': '#BC4CC7',
27 | '600': '#B042BA',
28 | '700': '#A238AD',
29 | '800': '#9430A0',
30 | '900': '#832588',
31 | A100: '#F4D6FF',
32 | A200: '#E3A8FF',
33 | A400: '#D27AFF',
34 | A700: '#C24CFF',
35 | };
36 |
37 | export const green: PaletteColorOptions = {
38 | '50': '#E0F2E9',
39 | '100': '#B3DEC3',
40 | '200': '#80C99D',
41 | '300': '#4DB578',
42 | '400': '#26A458',
43 | '500': '#00963A',
44 | '600': '#008932',
45 | '700': '#00792A',
46 | '800': '#006A22',
47 | '900': '#004F13',
48 | A100: '#A5FFB8',
49 | A200: '#78FF99',
50 | A400: '#4BFF7A',
51 | A700: '#1EFF5B',
52 | };
53 |
54 | export const red: PaletteColorOptions = {
55 | '50': '#FFEBEE',
56 | '100': '#FFCDD2',
57 | '200': '#EF9A9A',
58 | '300': '#E57373',
59 | '400': '#EF5350',
60 | '500': '#F44336',
61 | '600': '#E53935',
62 | '700': '#D32F2F',
63 | '800': '#C62828',
64 | '900': '#B71C1C',
65 | A100: '#FF8A80',
66 | A200: '#FF5252',
67 | A400: '#FF1744',
68 | A700: '#D50000',
69 | };
70 |
--------------------------------------------------------------------------------
/frontend/src/View/DashboardView/DashboardView.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@mui/material';
2 | import { Suspense } from 'react';
3 | import { getIsInGroup } from '~/api/Group/query';
4 | import { getSettings } from '~/api/Settings/query';
5 | import { LoadingView } from '../LoadingView/LoadingView';
6 | import { EventSection } from './Sections/EventSection';
7 | import { GroupProjectSection } from './Sections/GroupProjectSection';
8 | import { NoteSection } from './Sections/NoteSection';
9 | import { ProjectSection } from './Sections/ProjectSection';
10 | import { TodoSection } from './Sections/TodoSection';
11 |
12 | export const DashboardView = () => {
13 | const { data: group, isLoading: isLoadingGroup } = getIsInGroup();
14 | const { data: settings, isLoading } = getSettings();
15 |
16 | if (!settings || isLoading || isLoadingGroup || group === undefined)
17 | return ;
18 |
19 | const { routes } = settings;
20 |
21 | return (
22 |
29 | }>
30 |
37 | {routes['Todo'] && }
38 | {routes['Calendar'] && }
39 | {routes['Notes'] && }
40 | {routes['Projects'] && }
41 | {routes['Group Projects'] && group !== false && (
42 |
43 | )}
44 |
45 |
46 |
47 | );
48 | };
49 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/TodoView.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from '@mui/material';
2 | import { Main } from '~/component/Main';
3 | import { useRef, useState } from 'react';
4 | import { TodoPreview } from './TodoPreview';
5 | import { ProjectTodo } from '~/api/ProjectTodos/api';
6 | import { debounce } from 'lodash';
7 | import { NoTodoView } from './NoTodoView';
8 | import { TodoDrawer } from './TodoDrawer';
9 |
10 | export enum ViewMode {
11 | unfinished,
12 | completed,
13 | }
14 |
15 | export const TodoView = () => {
16 | const [viewMode, setViewMode] = useState(ViewMode.unfinished);
17 | const [selectedProject, setSelectedProject] = useState<
18 | ProjectTodo | undefined
19 | >(undefined);
20 |
21 | const pageNumber = 1;
22 | const [filter, setFilter] = useState('');
23 | const pageSize = 5;
24 |
25 | const handleFilter = useRef(
26 | debounce(
27 | (event: React.ChangeEvent) =>
28 | setFilter(event.target.value),
29 | 500,
30 | ),
31 | ).current;
32 |
33 | return (
34 |
35 |
44 |
45 | {selectedProject ? (
46 |
50 | ) : (
51 |
52 | )}
53 |
54 |
55 | );
56 | };
57 |
--------------------------------------------------------------------------------
/frontend/src/component/ProjectMenu/ProjectMenu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Menu from '@mui/material/Menu';
3 | import MenuItem from '@mui/material/MenuItem';
4 | import { IconButton } from '@mui/material';
5 | import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
6 | import CloseIcon from '@mui/icons-material/Close';
7 | import { ProjectDelete } from '~/component/ProjectDeleteDialog';
8 | import { DialogEdit } from '~/View/ProjectView/DialogEdit';
9 | import { Project } from '~/api/Projects/api';
10 | import { deleteProject } from '~/api/Projects/query';
11 |
12 | type Props = {
13 | project: Project;
14 | };
15 |
16 | export const ProjectMenu = ({ project }: Props) => {
17 | const [anchorEl, setAnchorEl] = React.useState(null);
18 | const { mutateAsync } = deleteProject();
19 | const open = Boolean(anchorEl);
20 | const handleClick = (event: React.MouseEvent) => {
21 | setAnchorEl(event.currentTarget);
22 | };
23 | const handleClose = () => {
24 | setAnchorEl(null);
25 | };
26 |
27 | return (
28 |
29 |
35 |
36 |
37 |
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/frontend/src/component/GroupProjectMenu/GroupProjectMenu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Menu from '@mui/material/Menu';
3 | import MenuItem from '@mui/material/MenuItem';
4 | import { IconButton } from '@mui/material';
5 | import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
6 | import CloseIcon from '@mui/icons-material/Close';
7 | import { ProjectDelete } from '~/component/ProjectDeleteDialog';
8 | import { GroupProject } from '~/api/Group/api';
9 | import { deleteGroupProject } from '~/api/GroupProjects/query';
10 | import { DialogEdit } from '~/View/GroupView/GroupProjectView/DialogEdit';
11 |
12 | type Props = {
13 | project: GroupProject;
14 | };
15 |
16 | export const GroupProjectMenu = ({ project }: Props) => {
17 | const [anchorEl, setAnchorEl] = React.useState(null);
18 | const { mutateAsync } = deleteGroupProject();
19 | const open = Boolean(anchorEl);
20 | const handleClick = (event: React.MouseEvent) => {
21 | setAnchorEl(event.currentTarget);
22 | };
23 | const handleClose = () => {
24 | setAnchorEl(null);
25 | };
26 |
27 | return (
28 |
29 |
35 |
36 |
37 |
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/frontend/src/api/Notes/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 | import { PaginationRequest, PaginationResponse } from '../pagination';
3 |
4 | export interface Note {
5 | noteID: number;
6 | title: string;
7 | content: string;
8 | createdAt: string;
9 | }
10 |
11 | type EditNote = {
12 | noteId: number;
13 | note: editProps;
14 | };
15 | type editProps = {
16 | title: string;
17 | content: string;
18 | };
19 |
20 | export const queryAllNotes = async (pagination : PaginationRequest) => {
21 | const response = await axiosInstance.get('/api/notes/getAllNotes',{
22 | params:{
23 | pageNumber: pagination.pageNumber,
24 | pageSize: pagination.pageSize,
25 | filter: pagination.filter
26 | }
27 | });
28 | return response.data as PaginationResponse;
29 | };
30 |
31 | export const postAddNote = async () => {
32 | return await axiosInstance.post('/api/notes/addNote');
33 | };
34 |
35 | export const queryNoteByID = async (noteId: number) => {
36 | const response = await axiosInstance.get(`/api/notes/getNoteById/${noteId}`);
37 | return response.data as Note;
38 | };
39 |
40 | export const editNoteById = async (note: EditNote) => {
41 | return await axiosInstance.patch(
42 | `/api/notes/updateNote/${note.noteId}`,
43 | note.note,
44 | );
45 | };
46 |
47 | export const deleteNoteById = async (noteId: number) => {
48 | return await axiosInstance.delete(`/api/notes/deleteNote/${noteId}`);
49 | };
50 |
51 | export const queryShareToken = async (noteId: number) => {
52 | const response = await axiosInstance.get(
53 | `/api/notes/getShareToken/${noteId}`,
54 | );
55 | return response.data as string;
56 | };
57 |
58 | export const queryNoteFromToken = async (token: string) => {
59 | const response = await axiosInstance.get(
60 | `/api/notes/getNoteFromToken/${token}`,
61 | );
62 | return response.data as Note;
63 | };
64 |
--------------------------------------------------------------------------------
/frontend/src/api/Calendar/query.ts:
--------------------------------------------------------------------------------
1 | import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2 | import { useSnackbar } from 'notistack';
3 | import {
4 | EditEvent,
5 | EventPagination,
6 | deleteEvent,
7 | patchEditEvent,
8 | postAddEvent,
9 | queryEventBetween,
10 | } from './api';
11 | import { t } from "@lingui/macro"
12 |
13 | export const getEventBetween = (pagination: EventPagination) => {
14 | return useQuery({
15 | queryKey: ['eventBetween', pagination],
16 | queryFn: () => queryEventBetween(pagination),
17 | });
18 | };
19 |
20 | export const mutateAddEvent = () => {
21 | const queryClient = useQueryClient();
22 | const { enqueueSnackbar } = useSnackbar();
23 | return useMutation({
24 | mutationKey: ['addEvent'],
25 | mutationFn: postAddEvent,
26 | onSuccess: () => {
27 | enqueueSnackbar(t({message:'Event added'}));
28 | queryClient.invalidateQueries({ queryKey: ['eventBetween'] });
29 | },
30 | });
31 | };
32 |
33 | export const mutateEditEvent = () => {
34 | const queryClient = useQueryClient();
35 | const { enqueueSnackbar } = useSnackbar();
36 | return useMutation({
37 | mutationKey: ['editEvent'],
38 | mutationFn: (event: EditEvent) => patchEditEvent(event),
39 | onSuccess: () => {
40 | enqueueSnackbar(t({message:'Event edited'}));
41 | queryClient.invalidateQueries({ queryKey: ['eventBetween'] });
42 | },
43 | });
44 | };
45 |
46 | export const mutateDeleteEvent = () => {
47 | const queryClient = useQueryClient();
48 | const { enqueueSnackbar } = useSnackbar();
49 | return useMutation({
50 | mutationKey: ['deleteEvent'],
51 | mutationFn: (eventId: number) => deleteEvent(eventId),
52 | onSuccess: () => {
53 | enqueueSnackbar(t({message:'Event deleted'}));
54 | queryClient.invalidateQueries({ queryKey: ['eventBetween'] });
55 | },
56 | });
57 | };
58 |
--------------------------------------------------------------------------------
/backend/backend/MapProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using backend.Dto.CalendarEvents;
3 | using backend.Dto.GroupProjects;
4 | using backend.Dto.GroupProjectTasks;
5 | using backend.Dto.Groups;
6 | using backend.Dto.Notes;
7 | using backend.Dto.Projects;
8 | using backend.Dto.ProjectTasks;
9 | using backend.Dto.ProjectTodo;
10 | using backend.Dto.Todos;
11 | using backend.Models;
12 |
13 | namespace backend
14 |
15 | {
16 | public class MapProfile : Profile
17 | {
18 | public MapProfile()
19 | {
20 | CreateMap();
21 | CreateMap();
22 | CreateMap();
23 | CreateMap()
24 | .ForMember(dest => dest.ProjectTaskID, opt => opt.MapFrom(src => src.GroupProjectTaskID))
25 | .ForMember(dest => dest.User, opt => opt.MapFrom(src => src.User != null ? src.User.Username : null))
26 | .ForMember(dest => dest.CanEdit, opt => opt.MapFrom((src, dest, destMember, context) =>
27 | {
28 | var currentUserId = context.Items["CurrentUserId"] as int?;
29 | var currentUserRole = context.Items["CurrentUserRole"] as Enums.GroupRole?;
30 |
31 | var isTaskOwner = src.User?.UserID== currentUserId;
32 | var isAdminOrModerator = currentUserRole == Enums.GroupRole.Admin || currentUserRole == Enums.GroupRole.Moderator;
33 |
34 | return isTaskOwner || isAdminOrModerator;
35 | }));
36 |
37 |
38 | CreateMap();
39 | CreateMap();
40 | CreateMap();
41 | CreateMap();
42 | CreateMap();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/TodoTask/CreateForm.tsx:
--------------------------------------------------------------------------------
1 | import { Box, TextField } from '@mui/material';
2 | import { todoModel } from './schema';
3 | import { UseFormReturn, useController } from 'react-hook-form';
4 | import { Trans } from '@lingui/macro';
5 |
6 | type Props = {
7 | onSubmit: (value: todoModel) => void;
8 | formContext: UseFormReturn;
9 | };
10 |
11 | export const CreateForm = ({ onSubmit, formContext }: Props) => {
12 | const {
13 | control,
14 | handleSubmit,
15 | formState: { errors },
16 | } = formContext;
17 |
18 | const title = useController({
19 | control: control,
20 | name: 'title',
21 | });
22 |
23 | const desc = useController({
24 | control: control,
25 | name: 'description',
26 | });
27 |
28 | return (
29 | onSubmit(data))}
40 | >
41 | Title}
44 | inputRef={title.field.ref}
45 | value={title.field.value}
46 | onChange={title.field.onChange}
47 | onBlur={title.field.onBlur}
48 | name={title.field.name}
49 | error={errors.title !== undefined}
50 | helperText={errors.title?.message}
51 | />
52 |
53 | Description}
56 | inputRef={desc.field.ref}
57 | value={desc.field.value}
58 | onChange={desc.field.onChange}
59 | onBlur={desc.field.onBlur}
60 | name={desc.field.name}
61 | error={errors.description !== undefined}
62 | helperText={errors.description?.message}
63 | />
64 |
65 | );
66 | };
67 |
--------------------------------------------------------------------------------
/frontend/src/View/GroupView/GroupProjectView/UsersTab.tsx:
--------------------------------------------------------------------------------
1 | import { Trans, t } from '@lingui/macro';
2 | import { Box, Typography, List, Divider } from '@mui/material';
3 | import { debounce } from 'lodash';
4 | import { useRef, useState } from 'react';
5 | import { GroupUser } from '~/api/Group/api';
6 | import { GroupUserCard } from '~/component/GroupUserCard';
7 | import { SearchField } from '~/component/SearchField';
8 |
9 | type Props = {
10 | users: GroupUser[];
11 | groupId: number;
12 | };
13 |
14 | export const UserTab = ({ users, groupId }: Props) => {
15 | const [filter, setFilter] = useState('');
16 |
17 | const handleFilter = useRef(
18 | debounce(
19 | (event: React.ChangeEvent) =>
20 | setFilter(event.target.value),
21 | 500,
22 | ),
23 | ).current;
24 | return (
25 | <>
26 |
33 |
34 | Explore Users
35 |
36 |
37 |
41 |
42 |
43 |
51 | {users
52 | .filter((a) =>
53 | a.username.toLocaleLowerCase().includes(filter.toLocaleLowerCase()),
54 | )
55 | .map((item) => (
56 |
57 |
58 |
59 |
60 | ))}
61 |
62 | >
63 | );
64 | };
65 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/TodoController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Todos;
2 | using backend.Interface;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace backend.Controllers
7 | {
8 | [Route("api/todos")]
9 | [ApiController]
10 | public class TodoController : ControllerBase
11 | {
12 | private readonly ITodoRepository _projectService;
13 | public TodoController(ITodoRepository projectService)
14 | {
15 | _projectService = projectService;
16 | }
17 |
18 | [HttpGet("getAllTodos/{todoId}")]
19 | [Authorize]
20 | public async Task>> getAllTodos([FromRoute] int todoId)
21 | {
22 | var project = await _projectService.GetAllTodos(todoId);
23 | return Ok(project);
24 | }
25 |
26 | [HttpPost("addTodo")]
27 | [Authorize]
28 | public async Task addTodo(AddTodoDto dto)
29 | {
30 | await _projectService.AddTodo(dto);
31 | return Ok();
32 | }
33 |
34 | [HttpPatch("updateTodo/{todoId}")]
35 | [Authorize]
36 | public async Task updateTodo(AddTodoDto dto, [FromRoute] int todoId)
37 | {
38 | await _projectService.UpdateTodo(dto, todoId);
39 | return Ok();
40 | }
41 |
42 | [HttpPatch("toggleStatus/{todoId}")]
43 | [Authorize]
44 | public async Task toggleTodo(ToggleTodoDto dto,[FromRoute] int todoId)
45 | {
46 | await _projectService.ToggleDone(dto,todoId);
47 | return Ok();
48 | }
49 |
50 | [HttpDelete("deleteTodo/{todoId}")]
51 | [Authorize]
52 | public async Task deleteTodo([FromRoute] int todoId)
53 | {
54 | await _projectService.DeleteTodo(todoId);
55 | return Ok();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/backend/backend/backend.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | <_ContentIncludedByDefault Remove="appsettings.json.example" />
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | all
27 | runtime; build; native; contentfiles; analyzers; buildtransitive
28 |
29 |
30 |
31 | all
32 | runtime; build; native; contentfiles; analyzers; buildtransitive
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/CalendarEventController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.CalendarEvents;
2 | using backend.Interface;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Mvc;
5 | using System.Text.Json;
6 |
7 | namespace backend.Controllers
8 | {
9 | [Route("api/calendar")]
10 | [ApiController]
11 | public class CalendarEventController : ControllerBase
12 | {
13 | private readonly ICalendarEventRepository _calendarService;
14 | public CalendarEventController(ICalendarEventRepository noteService)
15 | {
16 | _calendarService = noteService;
17 | }
18 |
19 | [HttpGet("getEventBetween")]
20 | [Authorize]
21 | public async Task>> GetAllEvents(
22 | [FromQuery] string from,
23 | [FromQuery] string to,
24 | [FromQuery] string colors)
25 | {
26 | var colorDict = JsonSerializer.Deserialize>(colors);
27 | var events = await _calendarService.GetAllEventsBetween(from, to, colorDict ?? new Dictionary());
28 | return Ok(events);
29 | }
30 |
31 | [HttpPost("addEvent")]
32 | [Authorize]
33 | public async Task addEvent(CalendarEventDto dto)
34 | {
35 | await _calendarService.AddEvent(dto);
36 | return Ok();
37 | }
38 |
39 | [HttpPatch("updateEvent/{eventId}")]
40 | [Authorize]
41 | public async Task updateNote(CalendarEventDto dto, [FromRoute] int eventId)
42 | {
43 | await _calendarService.UpdateEvent(dto, eventId);
44 | return Ok();
45 | }
46 |
47 | [HttpDelete("deleteEvent/{eventId}")]
48 | [Authorize]
49 | public async Task deleteNote([FromRoute] int eventId)
50 | {
51 | await _calendarService.DeleteEvent(eventId);
52 | return Ok();
53 | }
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/frontend/src/View/RegisterView/RegisterView.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar, Box, Button, Paper, Stack, Typography } from '@mui/material';
2 | import Logo from '~/assets/Logo.png';
3 | import { FORM_ID, RegisterForm } from './RegisterForm';
4 | import { registerFormSchema } from './schema';
5 | import { useForm } from 'react-hook-form';
6 | import { zodResolver } from '@hookform/resolvers/zod';
7 | import { NavLink } from 'react-router-dom';
8 | import { mutateUserRegister } from '~/api/User/query';
9 | import { Trans } from '@lingui/macro';
10 |
11 | export const RegisterView = () => {
12 | const form = useForm({
13 | defaultValues: {
14 | id: -1,
15 | username: '',
16 | email: '',
17 | password: '',
18 | passwordConfirm: '',
19 | },
20 | resolver: zodResolver(registerFormSchema),
21 | });
22 | const { mutateAsync } = mutateUserRegister();
23 |
24 | const handleSubmit = (data: registerFormSchema) => {
25 | mutateAsync(data);
26 | form.reset();
27 | };
28 |
29 | return (
30 |
41 |
42 |
43 |
44 |
45 |
46 | Register
47 |
48 |
49 |
50 |
53 |
56 |
57 |
58 |
59 | );
60 | };
61 |
--------------------------------------------------------------------------------
/frontend/src/View/NotesView/DialogDelete.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | Button,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | IconButton,
9 | } from '@mui/material';
10 | import { useState } from 'react';
11 | import DeleteIcon from '@mui/icons-material/Delete';
12 | import { deleteNote } from '~/api/Notes/query';
13 | import { Trans } from '@lingui/macro';
14 |
15 | type Props = {
16 | noteId: number | null;
17 | };
18 |
19 | export const DialogDelete = ({ noteId }: Props) => {
20 | const { mutateAsync } = deleteNote();
21 | const [open, setOpen] = useState(false);
22 |
23 | const handleClickOpen = () => {
24 | setOpen(true);
25 | };
26 |
27 | const handleClose = () => {
28 | setOpen(false);
29 | };
30 |
31 | return (
32 | <>
33 |
34 |
35 |
36 |
74 | >
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/frontend/src/View/CalendarView/DialogCreate.tsx:
--------------------------------------------------------------------------------
1 | import { zodResolver } from '@hookform/resolvers/zod';
2 | import { Trans } from '@lingui/macro';
3 | import {
4 | Button,
5 | Dialog,
6 | DialogActions,
7 | DialogContent,
8 | DialogTitle,
9 | useTheme,
10 | } from '@mui/material';
11 | import dayjs from 'dayjs';
12 | import { Dispatch } from 'react';
13 | import { useForm } from 'react-hook-form';
14 | import { mutateAddEvent } from '~/api/Calendar/query';
15 | import { CreateForm } from './CreateForm';
16 | import { eventModel, eventSchema } from './schema';
17 |
18 | type Props = {
19 | open: boolean;
20 | setOpen: Dispatch;
21 | day: dayjs.Dayjs;
22 | };
23 |
24 | export const DialogCreate = ({ open, setOpen, day }: Props) => {
25 | const { mutateAsync } = mutateAddEvent();
26 |
27 | const eventForm = useForm({
28 | defaultValues: {
29 | title: '',
30 | description: '',
31 | dateTime: day.format('DD.MM.YYYY'),
32 | eventColor: useTheme().palette.secondary.dark,
33 | },
34 | resolver: zodResolver(eventSchema),
35 | });
36 |
37 | const handleClose = () => {
38 | eventForm.reset();
39 | setOpen(false);
40 | };
41 |
42 | const handleSubmit = (data: eventModel) => {
43 | mutateAsync(data);
44 | handleClose();
45 | };
46 |
47 | return (
48 |
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/frontend/src/api/User/api.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { BASE_URL } from '~/config/constants';
3 | import { registerFormSchema } from '~/View/RegisterView/schema';
4 | import { loginFormSchema } from '~/View/LoginView/schema';
5 | import { userModel } from '~/component/UserBox/schema';
6 | import { axiosInstance as authInstance }from '../api'
7 | import {
8 | forgotPasswordModel,
9 | resetPasswordModel,
10 | } from '~/View/RegisterView/PasswordRenew/schema';
11 | import { axiosInstance as loggedInstance } from '../api';
12 |
13 | export interface MyAccount {
14 | username: string;
15 | email: string;
16 | groupName: string;
17 | image:string;
18 | }
19 |
20 | export interface AccessToken {
21 | accessToken: string;
22 | refreshToken: string;
23 | }
24 |
25 | export const axiosInstance = axios.create({ baseURL: BASE_URL });
26 |
27 | export const postUserRegister = async (userData: registerFormSchema) => {
28 | return await axiosInstance.post('/api/account/register', userData);
29 | };
30 |
31 | export const postUserLogin = async (userData: loginFormSchema) => {
32 | const response = await axiosInstance.post('/api/account/login', userData);
33 | return response.data as AccessToken;
34 | };
35 |
36 | export const postUserLogout = async () => {
37 | return await loggedInstance.post('/api/account/logout');
38 | };
39 |
40 | export const postForgotPassword = async (userData: forgotPasswordModel) => {
41 | return await axiosInstance.post('/api/account/forgotPassword', userData);
42 | };
43 |
44 | export const postResetPassword = async (userData: resetPasswordModel) => {
45 | return await axiosInstance.post('/api/account/resetPassword', userData);
46 | };
47 |
48 | export const postDeleteAccount = async () => {
49 | return await loggedInstance.delete('/api/account/deleteAccount');
50 | };
51 |
52 | export const queryMyAccount = async () => {
53 | const response = await loggedInstance.get('/api/account/getMyAccount');
54 | return response.data as MyAccount;
55 | };
56 |
57 | export const updateUser = async (userData: userModel) => {
58 | return await authInstance.patch('/api/account/updateAccount', userData);
59 | };
--------------------------------------------------------------------------------
/backend/backend/Controllers/ProjectController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Pagination;
2 | using backend.Dto.Projects;
3 | using backend.Interface;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace backend.Controllers
8 | {
9 | [Route("api/projects")]
10 | [ApiController]
11 | public class ProjectController : ControllerBase
12 | {
13 | private readonly IProjectRepository _projectService;
14 | public ProjectController(IProjectRepository projectService)
15 | {
16 | _projectService = projectService;
17 | }
18 |
19 | [HttpGet("getAllProjects")]
20 | [Authorize]
21 | public async Task>> getAllProjects([FromQuery] PaginationRequestDto paginationRequest)
22 | {
23 | var projects = await _projectService.GetAllProjects(paginationRequest);
24 | return Ok(projects);
25 | }
26 |
27 | [HttpGet("getProjectById/{projectId}")]
28 | [Authorize]
29 | public async Task> getProjectById([FromRoute] int projectId)
30 | {
31 | var project = await _projectService.GetProjectById(projectId);
32 | return Ok(project);
33 | }
34 | [HttpPost("addProject")]
35 | [Authorize]
36 | public async Task addProject(AddProjectDto dto)
37 | {
38 | await _projectService.AddProject(dto);
39 | return Ok();
40 | }
41 |
42 | [HttpPatch("updateProject/{projectId}")]
43 | [Authorize]
44 | public async Task updateProject(EditProjectDto dto, [FromRoute] int projectId)
45 | {
46 | await _projectService.UpdateProject(dto,projectId);
47 | return Ok();
48 | }
49 |
50 | [HttpDelete("deleteProject/{projectId}")]
51 | [Authorize]
52 | public async Task deleteProject([FromRoute] int projectId)
53 | {
54 | await _projectService.DeleteProject(projectId);
55 | return Ok();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/DialogDelete.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | Button,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | IconButton,
9 | } from '@mui/material';
10 | import { useState } from 'react';
11 | import DeleteIcon from '@mui/icons-material/Delete';
12 | import { deleteProjectTodo } from '~/api/ProjectTodos/query';
13 | import { Trans } from '@lingui/macro';
14 |
15 | type Props = {
16 | todoId: number | undefined;
17 | };
18 |
19 | export const DialogDelete = ({ todoId }: Props) => {
20 | const { mutateAsync } = deleteProjectTodo();
21 | const [open, setOpen] = useState(false);
22 |
23 | const handleClickOpen = () => {
24 | setOpen(true);
25 | };
26 |
27 | const handleClose = () => {
28 | setOpen(false);
29 | };
30 |
31 | return (
32 | <>
33 |
34 |
35 |
36 |
74 | >
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/frontend/src/View/LoginView/LoginForm.tsx:
--------------------------------------------------------------------------------
1 | import { Trans } from '@lingui/macro';
2 | import { Box, Stack, TextField } from '@mui/material';
3 | import { UseFormReturn, useController } from 'react-hook-form';
4 | import { PasswordField } from '~/component/PasswordField';
5 | import { loginFormSchema } from './schema';
6 |
7 | type loginFormProps = {
8 | onSubmit: (value: loginFormSchema) => void;
9 | formContext: UseFormReturn;
10 | };
11 |
12 | export const FORM_ID = 'login-form';
13 |
14 | export const LoginForm = ({ onSubmit, formContext }: loginFormProps) => {
15 | const {
16 | control,
17 | handleSubmit,
18 | formState: { errors },
19 | } = formContext;
20 |
21 | const email = useController({
22 | control: control,
23 | name: 'email',
24 | });
25 | const password = useController({
26 | control: control,
27 | name: 'password',
28 | });
29 |
30 | return (
31 | {
35 | onSubmit(data);
36 | })}
37 | >
38 |
39 | Email}
43 | type="text"
44 | autoComplete="off"
45 | name={email.field.name}
46 | value={email.field.value}
47 | onChange={email.field.onChange}
48 | onBlur={email.field.onBlur}
49 | inputRef={email.field.ref}
50 | error={errors.email !== undefined}
51 | helperText={errors.email?.message ?? ''}
52 | />
53 |
54 | Password}
57 | autoComplete="off"
58 | name={password.field.name}
59 | value={password.field.value}
60 | onChange={password.field.onChange}
61 | onBlur={password.field.onBlur}
62 | inputRef={password.field.ref}
63 | error={errors.password !== undefined}
64 | helperText={errors.password?.message ?? ''}
65 | />
66 |
67 |
68 | );
69 | };
70 |
--------------------------------------------------------------------------------
/frontend/src/api/ProjectTodos/api.ts:
--------------------------------------------------------------------------------
1 | import { axiosInstance } from '../api';
2 | import { PaginationRequest, PaginationResponse } from '../pagination';
3 |
4 | export interface ProjectTodo {
5 | projectTodoID: number;
6 | title: string;
7 | description: string;
8 | color: string;
9 | isDone: boolean;
10 | }
11 |
12 | export interface AddProjectTodo {
13 | title: string;
14 | description?: string;
15 | color: string;
16 | }
17 |
18 | export interface EditProjectTodo {
19 | projectId: number;
20 | project: AddProjectTodo;
21 | }
22 |
23 | export interface TodoById {
24 | title: string;
25 | description: string;
26 | color: string;
27 | todos: Todo[];
28 | }
29 |
30 | export interface Todo {
31 | todoID: number;
32 | title: string;
33 | description: string;
34 | isDone: boolean;
35 | }
36 |
37 | export interface TodoList {
38 | projects: ProjectTodo[];
39 | }
40 |
41 | export const queryProjectTodo = async (isDone: boolean,pagination : PaginationRequest) => {
42 | const response = await axiosInstance.get(`/api/projectTodos/getAllProjects`, {
43 | params:{
44 | isDone: isDone,
45 | pageNumber: pagination.pageNumber,
46 | pageSize: pagination.pageSize,
47 | filter: pagination.filter
48 | }
49 | });
50 | return response.data as PaginationResponse;;
51 | };
52 |
53 | export const queryProjectTodoById = async (projectId: number) => {
54 | const response = await axiosInstance.get(
55 | `/api/projectTodos/getById/${projectId}`,
56 | );
57 | return response.data as TodoById;
58 | };
59 |
60 | export const postAddProjectTodo = async (projectTodo: AddProjectTodo) => {
61 | return await axiosInstance.post('/api/projectTodos/addProject', projectTodo);
62 | };
63 |
64 | export const postEditProjectTodo = async (projectTodo: EditProjectTodo) => {
65 | return await axiosInstance.patch(
66 | `/api/projectTodos/updateProject/${projectTodo.projectId}`,
67 | projectTodo.project,
68 | );
69 | };
70 |
71 | export const postDeleteProjectTodo = async (projectTodoId: number) => {
72 | return await axiosInstance.delete(
73 | `/api/projectTodos/deleteProject/${projectTodoId}`,
74 | );
75 | };
76 |
--------------------------------------------------------------------------------
/frontend/src/component/UserBox/DialogRemoveAvatar.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | Button,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | IconButton,
9 | Tooltip,
10 | } from '@mui/material';
11 | import { useState } from 'react';
12 | import { Trans, t } from '@lingui/macro';
13 | import { mutateDeleteImage } from '~/api/Image/query';
14 | import DeleteIcon from '@mui/icons-material/Delete';
15 |
16 | type Props = {
17 | initialImage: string | null;
18 | };
19 |
20 | export const DialogRemoveAvatar = ({ initialImage }: Props) => {
21 | const [open, setOpen] = useState(false);
22 | const { mutateAsync: deleteImage } = mutateDeleteImage();
23 |
24 | const handleClickOpen = () => {
25 | setOpen(true);
26 | };
27 |
28 | const handleClose = () => {
29 | setOpen(false);
30 | };
31 |
32 | const handleDelete = () => {
33 | deleteImage(initialImage ?? '');
34 | setOpen(false);
35 | };
36 |
37 | return (
38 | <>
39 |
40 |
41 |
42 |
43 |
44 |
73 | >
74 | );
75 | };
76 |
--------------------------------------------------------------------------------
/backend/backend/Controllers/ProjectTodoController.cs:
--------------------------------------------------------------------------------
1 | using backend.Dto.Pagination;
2 | using backend.Dto.ProjectTodo;
3 | using backend.Interface;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace backend.Controllers
8 | {
9 | [Route("api/projectTodos")]
10 | [ApiController]
11 | public class ProjectTodoController : ControllerBase
12 | {
13 | private readonly IProjectTodoRepository _projectService;
14 | public ProjectTodoController(IProjectTodoRepository projectService)
15 | {
16 | _projectService = projectService;
17 | }
18 |
19 | [HttpGet("getAllProjects")]
20 | [Authorize]
21 | public async Task>> getAllProjects([FromQuery] bool isDone,[FromQuery] PaginationRequestDto paginationRequest)
22 | {
23 | var project = await _projectService.GetAllProjects(isDone, paginationRequest);
24 | return Ok(project);
25 | }
26 |
27 | [HttpGet("getById/{projectId}")]
28 | [Authorize]
29 | public async Task> getById([FromRoute] int projectId)
30 | {
31 | var project = await _projectService.GetProjectById(projectId);
32 | return Ok(project);
33 | }
34 |
35 | [HttpPost("addProject")]
36 | [Authorize]
37 | public async Task addProject(AddProjectTodoDto dto)
38 | {
39 | await _projectService.AddProject(dto);
40 | return Ok();
41 | }
42 |
43 | [HttpPatch("updateProject/{projectId}")]
44 | [Authorize]
45 | public async Task updateProject(AddProjectTodoDto dto, [FromRoute] int projectId)
46 | {
47 | await _projectService.UpdateProject(dto,projectId);
48 | return Ok();
49 | }
50 |
51 | [HttpDelete("deleteProject/{projectId}")]
52 | [Authorize]
53 | public async Task deleteProject([FromRoute] int projectId)
54 | {
55 | await _projectService.DeleteProject(projectId);
56 | return Ok();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/frontend/src/View/TodoView/TodoLink.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Typography } from '@mui/material';
2 | import { DrawerLink } from '../NotesView/DrawerLink';
3 | import { DialogEdit } from './DialogEdit';
4 | import { ProjectTodo } from '~/api/ProjectTodos/api';
5 | import { Dispatch, SetStateAction } from 'react';
6 |
7 | type Props = {
8 | project: ProjectTodo;
9 | selectedProject: ProjectTodo | undefined;
10 | setSelectedProject: Dispatch>;
11 | };
12 |
13 | export const TodoLink = ({
14 | project,
15 | selectedProject,
16 | setSelectedProject,
17 | }: Props) => {
18 | return (
19 | setSelectedProject(project)}
22 | display={'flex'}
23 | flexDirection={'column'}
24 | sx={{
25 | cursor: 'pointer',
26 | }}
27 | >
28 |
33 |
34 |
35 | ({
37 | textWrap: 'wrap',
38 | wordBreak: 'break-word',
39 | textDecoration: project.isDone ? 'line-through' : '',
40 | color: project.isDone ? theme.palette.text.secondary : '',
41 | })}
42 | >
43 | {project.title}
44 |
45 | ({
48 | textWrap: 'wrap',
49 | wordBreak: 'break-word',
50 | color: theme.palette.text.secondary,
51 | textDecoration: project.isDone ? 'line-through' : '',
52 | })}
53 | >
54 | {project.description}
55 |
56 |
57 | {project.projectTodoID === selectedProject?.projectTodoID && (
58 |
59 | )}
60 |
61 |
62 |
63 | );
64 | };
65 |
--------------------------------------------------------------------------------
/frontend/src/component/GroupUserCard/RemoveGroupDialog.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | Button,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | IconButton,
9 | Tooltip,
10 | } from '@mui/material';
11 | import { useState } from 'react';
12 | import DoDisturbIcon from '@mui/icons-material/DoDisturb';
13 | import { Trans } from '@lingui/macro';
14 | import { mutateDeleteGroup } from '~/api/Group/query';
15 |
16 | type Props = {
17 | groupId: number | null;
18 | };
19 |
20 | export const RemoveGroupDialog = ({ groupId }: Props) => {
21 | const { mutateAsync } = mutateDeleteGroup();
22 | const [open, setOpen] = useState(false);
23 |
24 | const handleClickOpen = () => {
25 | setOpen(true);
26 | };
27 |
28 | const handleClose = () => {
29 | setOpen(false);
30 | };
31 |
32 | return (
33 | <>
34 | Remove group}>
35 |
36 |
37 |
38 |
39 |
77 | >
78 | );
79 | };
80 |
--------------------------------------------------------------------------------