diff --git a/.prettierignore b/.prettierignore index 209260b..ab1293b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,6 @@ dist node_modules src-tauri/target src-tauri/gen -pnpm-lock.yaml \ No newline at end of file +pnpm-lock.yaml + +.github/* \ No newline at end of file diff --git a/Contributing.md b/Contributing.md index c6e38f5..159d2a1 100644 --- a/Contributing.md +++ b/Contributing.md @@ -2,17 +2,18 @@ Thank you for your interest in contributing to CommDesk. -***CommDesk*** is a desktop platform for *communities*, *clubs*, *organizations*, and *event teams*. Contributions that improve *usability*, *reliability*, *documentation*, *maintainability*, and *developer experience* are welcome. +**_CommDesk_** is a desktop platform for _communities_, _clubs_, _organizations_, and _event teams_. Contributions that improve _usability_, _reliability_, _documentation_, _maintainability_, and _developer experience_ are welcome. ## Before You Start Before making changes, please review the **existing documentation** and **project structure** to understand the current scope of the repository. This repository includes: -- A *React* + *TypeScript* **frontend** -- A *Tauri* **desktop application** layer -- A *Rust* backend under `src-tauri` -- *Documentation* for project scope, implementation status, and release flow + +- A _React_ + _TypeScript_ **frontend** +- A _Tauri_ **desktop application** layer +- A _Rust_ backend under `src-tauri` +- _Documentation_ for project scope, implementation status, and release flow ## Prerequisites @@ -23,7 +24,7 @@ To work on this project, you should have the following installed: - **Rust stable** (`rustup`, `cargo`) - The platform-specific dependencies required by **Tauri** -For *Linux*, *macOS*, and *Windows*, please ensure the appropriate system dependencies are installed before building the project. +For _Linux_, _macOS_, and _Windows_, please ensure the appropriate system dependencies are installed before building the project. ## Getting Started @@ -40,7 +41,7 @@ For *Linux*, *macOS*, and *Windows*, please ensure the appropriate system depend git clone https://github.com/NexGenStudioDev/CommDesk.git cd CommDesk pnpm install -```` +``` ### Run the Application @@ -58,33 +59,33 @@ pnpm dev ## Branch Naming -Use a *descriptive branch name* for your work. +Use a _descriptive branch name_ for your work. Recommended formats: -* `feature/` -* `fix/` -* `docs/` -* `refactor/` +- `feature/` +- `fix/` +- `docs/` +- `refactor/` Examples: -* `feature/member-search` -* `fix/login-error` -* `docs/update-readme` +- `feature/member-search` +- `fix/login-error` +- `docs/update-readme` ## Coding Standards -Please follow the existing *code style* and *project conventions*. +Please follow the existing _code style_ and _project conventions_. General expectations: -* Keep changes focused and minimal -* Write clear, readable, and maintainable code -* Follow the current folder structure and architecture -* Avoid introducing unnecessary dependencies -* Prefer small, reusable components and functions -* Keep frontend and backend changes consistent with the existing design +- Keep changes focused and minimal +- Write clear, readable, and maintainable code +- Follow the current folder structure and architecture +- Avoid introducing unnecessary dependencies +- Prefer small, reusable components and functions +- Keep frontend and backend changes consistent with the existing design Before submitting, format and lint your code where applicable. @@ -94,10 +95,10 @@ All contributions should be tested locally before submission. At minimum, verify: -* The application starts successfully -* The change works as intended -* Existing functionality is not broken -* The project passes linting and build checks where relevant +- The application starts successfully +- The change works as intended +- Existing functionality is not broken +- The project passes linting and build checks where relevant Useful commands: @@ -113,34 +114,34 @@ If your contribution affects desktop packaging or Rust functionality, test the r Before opening a new issue: -* Search existing issues to avoid duplicates -* Use a clear and descriptive title -* Include steps to reproduce the problem -* Add screenshots, logs, or environment details when relevant +- Search existing issues to avoid duplicates +- Use a clear and descriptive title +- Include steps to reproduce the problem +- Add screenshots, logs, or environment details when relevant A good bug report should explain: -* What you expected to happen -* What actually happened -* How to reproduce the issue -* Which platform and version you used +- What you expected to happen +- What actually happened +- How to reproduce the issue +- Which platform and version you used ## Pull Request Guidelines When opening a pull request, please ensure that: -* The pull request has a clear title -* The description explains what was changed and why -* Related issues are linked when applicable -* The changes are limited to a single purpose where possible -* The branch is up to date with the target branch before submission +- The pull request has a clear title +- The description explains what was changed and why +- Related issues are linked when applicable +- The changes are limited to a single purpose where possible +- The branch is up to date with the target branch before submission A strong pull request should include: -* A short summary of the change -* Screenshots or recordings for UI updates -* Testing notes -* Any relevant context for reviewers +- A short summary of the change +- Screenshots or recordings for UI updates +- Testing notes +- Any relevant context for reviewers ## Commit Messages @@ -148,10 +149,10 @@ Use concise and meaningful commit messages. Recommended style: -* `feat: add member search` -* `fix: resolve updater issue` -* `docs: improve contribution guide` -* `refactor: simplify event module` +- `feat: add member search` +- `fix: resolve updater issue` +- `docs: improve contribution guide` +- `refactor: simplify event module` ## Security and Secrets @@ -165,10 +166,10 @@ Please keep discussions respectful, constructive, and professional. Be considerate in: -* Issues -* Pull requests -* Code reviews -* Documentation discussions +- Issues +- Pull requests +- Code reviews +- Documentation discussions This project follows a [code of conduct](CODE_OF_CONDUCT.md). All contributors are expected to follow it. @@ -178,4 +179,4 @@ Maintainers may request changes before merging a pull request. Please respond to ## License -**By contributing to CommDesk, you agree that your contributions will be made under the same [License](LICENSE) as the project.** \ No newline at end of file +**By contributing to CommDesk, you agree that your contributions will be made under the same [License](LICENSE) as the project.** diff --git a/docs/AUTO_UPDATE_SETUP_GUIDE.md b/docs/AUTO_UPDATE_SETUP_GUIDE.md index 81e8ca2..d154703 100644 --- a/docs/AUTO_UPDATE_SETUP_GUIDE.md +++ b/docs/AUTO_UPDATE_SETUP_GUIDE.md @@ -1,4 +1,4 @@ -# 🚀 CommDesk Auto-Update System +# 🚀 CommDesk Auto-Update System # Enterprise-Grade Auto-Update Infrastructure for Tauri, Snap, Flathub, AppImage, Windows & macOS @@ -6,32 +6,31 @@ This document is the complete source of truth for implementing, deploying, secur It covers: -* Tauri auto-updater -* GitHub Releases integration -* Snap auto-updates -* Flathub auto-updates -* Release signing -* CI/CD automation -* Rollback systems -* Security verification -* Production deployment -* Monitoring & analytics -* Enterprise release workflows +- Tauri auto-updater +- GitHub Releases integration +- Snap auto-updates +- Flathub auto-updates +- Release signing +- CI/CD automation +- Rollback systems +- Security verification +- Production deployment +- Monitoring & analytics +- Enterprise release workflows Reference architecture and implementation details: - --- # 🧠 What Is Auto-Update? Auto-update allows CommDesk to: -* Detect new releases automatically -* Download updates securely -* Verify signatures -* Install updates safely -* Restart into the latest version +- Detect new releases automatically +- Download updates securely +- Verify signatures +- Install updates safely +- Restart into the latest version without requiring users to manually reinstall the app. @@ -93,11 +92,11 @@ Users Every update must be: -* Signed -* Verified -* Version validated -* Integrity checked -* Securely downloaded +- Signed +- Verified +- Version validated +- Integrity checked +- Securely downloaded --- @@ -262,11 +261,11 @@ latest.json This latest.json is responsible for telling the Tauri updater: -* what version is available -* what notes to show the user -* when the release was published -* which download URL to use for each platform -* which signature file to verify before installation +- what version is available +- what notes to show the user +- when the release was published +- which download URL to use for each platform +- which signature file to verify before installation Before publishing latest.json, also create the matching signature file for each build artifact: @@ -279,8 +278,6 @@ minisign -S \ The resulting .sig file must be uploaded with the release and its signature content must be referenced inside latest.json. - - --- # Example @@ -348,8 +345,7 @@ Restart app # React Example ```tsx id="jlwm8g" -const { shouldUpdate, manifest } = -await checkForUpdates(); +const { shouldUpdate, manifest } = await checkForUpdates(); if (shouldUpdate) { await installUpdate(); @@ -361,11 +357,11 @@ if (shouldUpdate) { # Recommended Features -* Update notification dialog -* Release notes -* Progress bar -* Retry support -* Restart button +- Update notification dialog +- Release notes +- Progress bar +- Retry support +- Restart button --- @@ -375,10 +371,10 @@ if (shouldUpdate) { Snapd automatically: -* Checks every few hours -* Downloads updates -* Installs safely -* Allows rollback +- Checks every few hours +- Downloads updates +- Installs safely +- Allows rollback --- @@ -394,7 +390,7 @@ sudo apt install snapcraft ```yaml id="jlwm5c" name: commdesk -version: '1.0.0' +version: "1.0.0" grade: stable confinement: strict @@ -547,12 +543,12 @@ User receives update GitHub Actions should: -* Build app -* Sign binaries -* Generate latest.json -* Upload GitHub release -* Upload Snap -* Build Flatpak +- Build app +- Sign binaries +- Generate latest.json +- Upload GitHub release +- Upload Snap +- Build Flatpak --- @@ -679,11 +675,11 @@ CommDesk.old.AppImage Track: -* Update success rate -* Failed installs -* Download stats -* Adoption rate -* Crash reports +- Update success rate +- Failed installs +- Download stats +- Adoption rate +- Crash reports --- @@ -691,7 +687,7 @@ Track: ```ts id="jlwm4y" trackUpdate("installed", { - version: "1.0.0" + version: "1.0.0", }); ``` @@ -739,22 +735,22 @@ trackUpdate("installed", { ## Before Release -* [ ] Version updated -* [ ] CHANGELOG updated -* [ ] Tests passing -* [ ] Binaries signed -* [ ] latest.json generated -* [ ] GitHub release ready +- [ ] Version updated +- [ ] CHANGELOG updated +- [ ] Tests passing +- [ ] Binaries signed +- [ ] latest.json generated +- [ ] GitHub release ready --- ## After Release -* [ ] GitHub release verified -* [ ] Snap uploaded -* [ ] Flathub updated -* [ ] Update notification tested -* [ ] Metrics monitored +- [ ] GitHub release verified +- [ ] Snap uploaded +- [ ] Flathub updated +- [ ] Update notification tested +- [ ] Metrics monitored --- @@ -764,10 +760,10 @@ trackUpdate("installed", { ## Causes -* Invalid latest.json -* Wrong signature -* Incorrect version format -* Broken URLs +- Invalid latest.json +- Wrong signature +- Incorrect version format +- Broken URLs --- @@ -818,4 +814,3 @@ Users Receive Secure Updates ``` --- - diff --git a/docs/FLATPAK_BUILD_GUIDE.md b/docs/FLATPAK_BUILD_GUIDE.md index 4121549..63ed913 100644 --- a/docs/FLATPAK_BUILD_GUIDE.md +++ b/docs/FLATPAK_BUILD_GUIDE.md @@ -4,29 +4,29 @@ CommDesk is designed as a modern large-scale desktop platform built using: -* Tauri -* Rust -* React -* TypeScript -* Vite +- Tauri +- Rust +- React +- TypeScript +- Vite This guide explains how to package, test, optimize, and distribute CommDesk for Linux using: -* Flatpak -* Snap -* AppImage -* Native Linux bundles -* Enterprise-grade release workflows +- Flatpak +- Snap +- AppImage +- Native Linux bundles +- Enterprise-grade release workflows --- # 📦 Supported Linux Distribution Formats -| Format | Best For | Sandbox | Auto Update | Store Support | -| -------- | ---------------------------- | -------- | ----------- | --------------- | -| Flatpak | Universal Linux Distribution | ✅ Strong | ✅ | Flathub | -| Snap | Ubuntu Ecosystem | ✅ Strong | ✅ | Snap Store | -| AppImage | Portable Distribution | ❌ | ❌ Manual | Direct Download | +| Format | Best For | Sandbox | Auto Update | Store Support | +| -------- | ---------------------------- | --------- | ----------- | --------------- | +| Flatpak | Universal Linux Distribution | ✅ Strong | ✅ | Flathub | +| Snap | Ubuntu Ecosystem | ✅ Strong | ✅ | Snap Store | +| AppImage | Portable Distribution | ❌ | ❌ Manual | Direct Download | | DEB | Debian/Ubuntu | ❌ | Manual/APT | Native | | RPM | Fedora/RHEL | ❌ | Manual/DNF | Native | @@ -40,11 +40,11 @@ This guide explains how to package, test, optimize, and distribute CommDesk for Best for: -* Security -* Sandboxing -* Enterprise deployment -* Cross-distro support -* Flathub visibility +- Security +- Sandboxing +- Enterprise deployment +- Cross-distro support +- Flathub visibility --- @@ -54,10 +54,10 @@ Best for: Best for: -* Portable usage -* No installation required -* Quick testing -* Offline environments +- Portable usage +- No installation required +- Quick testing +- Offline environments --- @@ -67,9 +67,9 @@ Best for: Best for: -* Ubuntu ecosystem -* Auto updates -* Canonical Store distribution +- Ubuntu ecosystem +- Auto updates +- Canonical Store distribution --- @@ -175,8 +175,8 @@ cargo --version Recommended: -* Node.js 20+ -* pnpm latest +- Node.js 20+ +- pnpm latest Install: @@ -210,11 +210,11 @@ commdesk/ Flatpak provides: -* Sandboxed execution -* Cross-distribution support -* Secure permissions -* Runtime dependency isolation -* Enterprise deployment compatibility +- Sandboxed execution +- Cross-distribution support +- Secure permissions +- Runtime dependency isolation +- Enterprise deployment compatibility --- @@ -271,11 +271,7 @@ org.commdesk.CommDesk.json { "name": "commdesk", "buildsystem": "simple", - "build-commands": [ - "pnpm install", - "pnpm build", - "cargo build --release" - ], + "build-commands": ["pnpm install", "pnpm build", "cargo build --release"], "sources": [ { "type": "dir", @@ -390,11 +386,11 @@ flatpak run org.commdesk.CommDesk Snap provides: -* Automatic updates -* Ubuntu ecosystem integration -* Canonical Store support -* Sandboxing -* Rollback support +- Automatic updates +- Ubuntu ecosystem integration +- Canonical Store support +- Sandboxing +- Rollback support --- @@ -421,7 +417,7 @@ mkdir snap ```yaml name: commdesk base: core24 -version: '1.0.0' +version: "1.0.0" summary: CommDesk Community Management Platform description: | CommDesk is a modern community and event management platform. @@ -484,10 +480,10 @@ snap run commdesk Best for: -* Portable distribution -* No installation -* Easy testing -* Direct downloads +- Portable distribution +- No installation +- Easy testing +- Direct downloads --- @@ -591,32 +587,32 @@ build: { # Build Validation -* [ ] TypeScript passes -* [ ] Rust build passes -* [ ] Flatpak launches -* [ ] Snap launches -* [ ] AppImage launches -* [ ] GPU acceleration works -* [ ] Network APIs work -* [ ] File system permissions work +- [ ] TypeScript passes +- [ ] Rust build passes +- [ ] Flatpak launches +- [ ] Snap launches +- [ ] AppImage launches +- [ ] GPU acceleration works +- [ ] Network APIs work +- [ ] File system permissions work --- # Security Validation -* [ ] Sandbox tested -* [ ] No unnecessary permissions -* [ ] Production env variables secured -* [ ] Secrets removed from frontend +- [ ] Sandbox tested +- [ ] No unnecessary permissions +- [ ] Production env variables secured +- [ ] Secrets removed from frontend --- # Performance Validation -* [ ] Cold start benchmark -* [ ] Memory profiling -* [ ] CPU usage profiling -* [ ] Large dataset rendering test +- [ ] Cold start benchmark +- [ ] Memory profiling +- [ ] CPU usage profiling +- [ ] Large dataset rendering test --- @@ -669,12 +665,12 @@ jobs: Ensure: -* App icons added -* Metadata complete -* Screenshots prepared -* License included -* Permissions minimized -* Stable builds verified +- App icons added +- Metadata complete +- Screenshots prepared +- License included +- Permissions minimized +- Stable builds verified --- @@ -682,11 +678,11 @@ Ensure: | Asset | Required | | ------------ | -------- | -| App Icon | ✅ | -| Screenshots | ✅ | -| AppData XML | ✅ | -| LICENSE | ✅ | -| Desktop File | ✅ | +| App Icon | ✅ | +| Screenshots | ✅ | +| AppData XML | ✅ | +| LICENSE | ✅ | +| Desktop File | ✅ | --- @@ -698,11 +694,11 @@ Ensure: # 📚 Useful Resources -* [Flatpak Documentation](https://docs.flatpak.org/?utm_source=chatgpt.com) -* [Snapcraft Docs](https://snapcraft.io/docs?utm_source=chatgpt.com) -* [Tauri Distribution Guide](https://tauri.app/distribute/?utm_source=chatgpt.com) -* [AppImage Docs](https://docs.appimage.org/?utm_source=chatgpt.com) -* [Flathub](https://flathub.org/?utm_source=chatgpt.com) +- [Flatpak Documentation](https://docs.flatpak.org/?utm_source=chatgpt.com) +- [Snapcraft Docs](https://snapcraft.io/docs?utm_source=chatgpt.com) +- [Tauri Distribution Guide](https://tauri.app/distribute/?utm_source=chatgpt.com) +- [AppImage Docs](https://docs.appimage.org/?utm_source=chatgpt.com) +- [Flathub](https://flathub.org/?utm_source=chatgpt.com) --- @@ -718,4 +714,3 @@ Ensure: | DEB/RPM | Enterprise | --- - diff --git a/docs/PRODUCTION_DEPLOYMENT.md b/docs/PRODUCTION_DEPLOYMENT.md index 296437e..103bd84 100644 --- a/docs/PRODUCTION_DEPLOYMENT.md +++ b/docs/PRODUCTION_DEPLOYMENT.md @@ -2,19 +2,18 @@ ## Overview - The goal is to transform CommDesk into a fully production-ready desktop platform capable of large-scale Linux distribution through: -* Flatpak -* Snap -* AppImage -* DEB -* RPM -* GitHub Releases -* Flathub -* Snap Store +- Flatpak +- Snap +- AppImage +- DEB +- RPM +- GitHub Releases +- Flathub +- Snap Store -Reference documentation: +Reference documentation: --- @@ -22,15 +21,15 @@ Reference documentation: ## Primary Goals -* Add professional Linux distribution support -* Introduce production deployment workflows -* Add enterprise-grade release architecture -* Standardize build systems -* Implement secure auto-update infrastructure -* Create scalable CI/CD pipelines -* Add cross-platform packaging strategy -* Improve release maintainability -* Prepare CommDesk for public distribution +- Add professional Linux distribution support +- Introduce production deployment workflows +- Add enterprise-grade release architecture +- Standardize build systems +- Implement secure auto-update infrastructure +- Create scalable CI/CD pipelines +- Add cross-platform packaging strategy +- Improve release maintainability +- Prepare CommDesk for public distribution --- @@ -38,27 +37,25 @@ Reference documentation: ## Linux -* Flatpak -* Snap -* AppImage -* DEB -* RPM +- Flatpak +- Snap +- AppImage +- DEB +- RPM ## Windows -* NSIS -* MSI -* Portable EXE +- NSIS +- MSI +- Portable EXE ## macOS -* DMG -* App Bundle +- DMG +- App Bundle --- - - # 🟦 Flatpak Infrastructure ## Add Flatpak Manifest @@ -71,13 +68,13 @@ org.commdesk.CommDesk.json ## Required Features -* Sandboxed runtime -* Flathub compatibility -* GPU acceleration support -* Network permissions -* Wayland + X11 support -* Rust SDK extensions -* Node.js SDK extensions +- Sandboxed runtime +- Flathub compatibility +- GPU acceleration support +- Network permissions +- Wayland + X11 support +- Rust SDK extensions +- Node.js SDK extensions --- @@ -107,11 +104,11 @@ snap/snapcraft.yaml ## Required Features -* Strict confinement -* Network permissions -* OpenGL support -* Wayland/X11 support -* Auto-update compatibility +- Strict confinement +- Network permissions +- OpenGL support +- Wayland/X11 support +- Auto-update compatibility --- @@ -139,12 +136,12 @@ src-tauri/target/release/bundle/appimage/ Implement: -* Tauri updater -* GitHub Releases integration -* Signed updates -* Release manifests -* Version verification -* Secure update validation +- Tauri updater +- GitHub Releases integration +- Signed updates +- Release manifests +- Version verification +- Secure update validation --- @@ -160,11 +157,11 @@ release.pub ## Required Features -* Silent background downloads -* Update notifications -* Signature verification -* Rollback safety -* Cross-platform updates +- Silent background downloads +- Update notifications +- Signature verification +- Rollback safety +- Cross-platform updates --- @@ -174,23 +171,23 @@ release.pub ### Linux -* Flatpak sandboxing -* Snap confinement -* Minimal filesystem access +- Flatpak sandboxing +- Snap confinement +- Minimal filesystem access ### Updates -* Minisign signatures -* Binary verification -* Release integrity validation +- Minisign signatures +- Binary verification +- Release integrity validation ### Windows -* Code signing support +- Code signing support ### macOS -* Apple notarization preparation +- Apple notarization preparation --- @@ -215,29 +212,29 @@ release.pub ### Validation -* TypeScript checks -* ESLint checks -* Rust checks -* Build verification -* Packaging verification +- TypeScript checks +- ESLint checks +- Rust checks +- Build verification +- Packaging verification ### Build Matrix -* Ubuntu -* Windows -* macOS +- Ubuntu +- Windows +- macOS ### Artifacts Upload: -* AppImage -* Flatpak bundle -* Snap package -* DEB -* RPM -* MSI -* DMG +- AppImage +- Flatpak bundle +- Snap package +- DEB +- RPM +- MSI +- DMG --- @@ -272,9 +269,9 @@ scripts/update-version.sh Capabilities: -* Update all version files -* Validate consistency -* Prevent release mismatch +- Update all version files +- Validate consistency +- Prevent release mismatch --- @@ -284,12 +281,12 @@ Capabilities: Required Sizes: -* 32x32 -* 64x64 -* 128x128 -* 256x256 -* ICO -* ICNS +- 32x32 +- 64x64 +- 128x128 +- 256x256 +- ICO +- ICNS --- @@ -306,9 +303,9 @@ src-tauri/icons/ Include: -* Logo generation workflow -* Export best practices -* Platform-specific icon requirements +- Logo generation workflow +- Export best practices +- Platform-specific icon requirements --- @@ -330,10 +327,10 @@ playwright ### Production Validation -* Flatpak sandbox tests -* AppImage runtime tests -* Snap permission tests -* Auto-update tests +- Flatpak sandbox tests +- AppImage runtime tests +- Snap permission tests +- Auto-update tests --- @@ -358,10 +355,10 @@ opt-level = "z" Add: -* Code splitting -* Lazy loading -* Vendor chunking -* Production minification +- Code splitting +- Lazy loading +- Vendor chunking +- Production minification --- @@ -371,14 +368,14 @@ Add: ### Crash Reporting -* Rust panic reporting -* Frontend error tracking +- Rust panic reporting +- Frontend error tracking ### Update Analytics -* Update adoption rate -* Failed update tracking -* Release stability metrics +- Update adoption rate +- Failed update tracking +- Release stability metrics --- @@ -386,14 +383,14 @@ Add: All deployment docs must include: -* Prerequisites -* Installation -* Build commands -* Release workflow -* Security recommendations -* Troubleshooting -* CI/CD examples -* Production best practices +- Prerequisites +- Installation +- Build commands +- Release workflow +- Security recommendations +- Troubleshooting +- CI/CD examples +- Production best practices --- @@ -401,11 +398,11 @@ All deployment docs must include: ## DO NOT -* Hardcode secrets -* Commit private signing keys -* Disable security checks -* Use unrestricted filesystem permissions -* Skip binary verification +- Hardcode secrets +- Commit private signing keys +- Disable security checks +- Use unrestricted filesystem permissions +- Skip binary verification --- @@ -413,37 +410,36 @@ All deployment docs must include: ## Packaging -* [ ] Flatpak builds successfully -* [ ] Snap builds successfully -* [ ] AppImage builds successfully -* [ ] DEB builds successfully -* [ ] RPM builds successfully +- [ ] Flatpak builds successfully +- [ ] Snap builds successfully +- [ ] AppImage builds successfully +- [ ] DEB builds successfully +- [ ] RPM builds successfully --- ## Security -* [ ] Auto-update signing works -* [ ] Signature verification works -* [ ] Sandboxing validated -* [ ] Minimal permissions enforced +- [ ] Auto-update signing works +- [ ] Signature verification works +- [ ] Sandboxing validated +- [ ] Minimal permissions enforced --- ## CI/CD -* [ ] Multi-platform builds work -* [ ] Release workflows automated -* [ ] Artifact uploads work -* [ ] Release tagging works +- [ ] Multi-platform builds work +- [ ] Release workflows automated +- [ ] Artifact uploads work +- [ ] Release tagging works --- ## Documentation -* [ ] Linux packaging docs completed -* [ ] Production deployment docs completed -* [ ] Auto-update docs completed -* [ ] Troubleshooting docs completed -* [ ] Release workflow documented - +- [ ] Linux packaging docs completed +- [ ] Production deployment docs completed +- [ ] Auto-update docs completed +- [ ] Troubleshooting docs completed +- [ ] Release workflow documented diff --git a/docs/Permission-Based-UI-Rendering.md b/docs/Permission-Based-UI-Rendering.md new file mode 100644 index 0000000..d7db8f5 --- /dev/null +++ b/docs/Permission-Based-UI-Rendering.md @@ -0,0 +1,661 @@ +# Permission-Based UI Rendering + +## Overview + +CommDesk uses a permission-based UI rendering system to decide which pages, buttons, actions, and management controls should be visible to a user. + +This implementation combines: + +- `TanStack Query` for fetching and caching permissions +- `Redux Toolkit` for storing permission state globally +- custom permission hooks for clean access checks +- reusable boundary and gate components for page-level and inline authorization + +The goal is simple: + +- show only the UI a user is allowed to use +- keep permission logic centralized +- make feature code easy to read and easy to extend +- avoid duplicated permission-check code across screens + +This document explains why we use this system, how it works, and how to use it correctly in production code. + +--- + +## Why We Use Permission-Based UI Rendering + +### 1. Better security posture at the UI layer + +The UI should not invite users to click actions they cannot perform. + +Examples: + +- hide `Create Event` if the user cannot create events +- hide `Delete Member` if the user cannot delete members +- hide `Send Email` if the user does not have contact email access + +Important: + +UI authorization is not a replacement for backend authorization. The backend must still validate permissions for every protected API action. + +### 2. Better user experience + +Without permission-aware rendering: + +- users see buttons that fail later +- users get confusing error messages after clicking +- layout becomes noisy and misleading + +With permission-aware rendering: + +- the interface feels focused +- users only see relevant actions +- restricted areas can provide clear guidance instead of dead ends + +### 3. Better maintainability + +If every component checks permissions differently, the codebase becomes fragile. + +This system keeps permission logic in one place so we can: + +- update permission behavior globally +- reuse hooks and boundary components +- keep feature components readable + +--- + +## High-Level Architecture + +The permission system lives in `src/permissions/`. + +### Main files + +- `src/permissions/constants.ts` + Contains permission keys such as `event:create` and `member:view`. + +- `src/permissions/permission.service.ts` + Fetches permissions for the current user and defines query keys. + +- `src/permissions/PermissionBootstrap.tsx` + Runs once near app startup, loads permissions through React Query, and syncs them into Redux. + +- `src/store/permissionsSlice.ts` + Stores granted permissions plus loading, error, and sync metadata. + +- `src/permissions/useAuthorization.tsx` + Exposes reusable hooks for permission checks: + `useAuthorization`, `usePermissionMap`, and `PermissionGate`. + +- `src/permissions/PermissionBoundary.tsx` + Handles page-level permission states: + loading, allowed, unauthorized. + +- `src/permissions/PermissionLoading.tsx` + Provides a clean loading state while access is being validated. + +- `src/permissions/AccessDenied.tsx` + Provides a reusable unauthorized state. + +- `src/permissions/selectors.ts` + Central Redux selectors for permission state. + +- `src/permissions/utils.ts` + Shared helpers for normalizing permission input and evaluating access. + +--- + +## Data Flow + +### Step 1: App starts + +`PermissionBootstrap` mounts near the app root in `src/main.tsx`. + +### Step 2: Permissions are fetched + +The bootstrap component uses `TanStack Query` to fetch permissions for the current user role. + +### Step 3: Permissions are cached + +React Query caches the result and prevents unnecessary refetching during the cache window. + +### Step 4: Permissions are stored globally + +Once fetched, permissions are written into Redux so any component can read them quickly without re-implementing fetch logic. + +### Step 5: Components consume permissions + +Components use: + +- `PermissionBoundary` for full-page access control +- `PermissionGate` for showing or hiding small UI sections +- `usePermissionMap` for components with multiple action buttons +- `useAuthorization` for lower-level custom logic + +--- + +## Permission Constants + +The system uses named constants instead of hard-coded strings. + +Example: + +```ts +export const Event_Permissions = { + CREATE_EVENT: "event:create", + UPDATE_EVENT: "event:update", + DELETE_EVENT: "event:delete", + VIEW_EVENT: "event:view", + PUBLISH_EVENT: "event:publish", + JOIN_EVENT: "event:join", + LEAVE_EVENT: "event:leave", +} as const; +``` + +Why this matters: + +- avoids typos +- improves autocomplete +- makes refactoring safer +- keeps permission usage consistent across modules + +--- + +## When to Use Each API + +### `PermissionBoundary` + +Use for page-level or section-level protection when you need: + +- a loading state +- an unauthorized fallback +- a protected content area + +Example: + +```tsx +} + unauthorizedFallback={ + + } +> + + +``` + +Use this when an entire page or major content area depends on access. + +### `PermissionGate` + +Use for small inline UI blocks such as: + +- buttons +- cards +- menu items +- action groups + +Example: + +```tsx + + } +{canDelete && } +``` + +--- + +## Production Guidelines + +### 1. Never hard-code permission strings inside feature components + +Do this: + +```tsx +permission={Event_Permissions.CREATE_EVENT} +``` + +Not this: + +```tsx +permission="event:create" +``` + +### 2. Prefer boundaries for pages + +If an entire page depends on permission, use `PermissionBoundary` instead of manual `if` chains. + +This keeps page code cleaner and more consistent. + +### 3. Prefer `usePermissionMap` for action-heavy components + +This is more maintainable than calling `useAuthorization` many times in the same component. + +### 4. Keep unauthorized UI honest + +Avoid showing: + +- fake action icons +- disabled-looking controls that still work +- menus with no valid action + +If a user cannot perform an action, either: + +- hide it +- or present a clear read-only state + +### 5. Handle loading intentionally + +Do not render unauthorized fallbacks before permission loading completes. + +That creates a flash of wrong content. + +Use: + +- `PermissionBoundary` with `loadingFallback` +- or `PermissionGate` with `loadingFallback` +- or `useAuthorization` with `isLoading` + +### 6. Keep backend and frontend authorization aligned + +Frontend permissions improve UX. +Backend permissions enforce security. + +Both are required. + +--- + +## Why Redux and React Query Together + +This is a common question. + +### Why not only React Query? + +React Query is excellent for: + +- fetching +- caching +- server-state freshness + +But UI code often wants lightweight global reads without repeating query usage everywhere. + +### Why not only Redux? + +Redux is excellent for: + +- global synchronous access +- predictable state transitions +- selectors + +But it is not the best tool alone for server data fetching and caching. + +### Why combine them? + +We use: + +- React Query for the async fetch lifecycle and caching +- Redux for simple app-wide access to resolved permission state + +This gives us: + +- centralized fetching +- consistent cached results +- simple UI access anywhere in the app + +--- + +## Current Role Mapping + +At the moment, permissions are resolved from a role-to-permission map in `permission.service.ts`. + +This works well for local development and UI scaffolding. + +Example: + +- `Admin` gets all permissions +- `Member` gets a limited set + +In a production backend integration, this file should fetch permissions from a real API instead of using mock role mapping. + +--- + +## How to Add a New Permission + +### Step 1: Add the constant + +Add it to the correct permission group in `src/permissions/constants.ts`. + +Example: + +```ts +export const Project_Permissions = { + VIEW_PROJECT: "project:view", + CREATE_PROJECT: "project:create", + ARCHIVE_PROJECT: "project:archive", +} as const; +``` + +### Step 2: Add it to the permission source + +Update the role mapping or backend response logic in `permission.service.ts`. + +### Step 3: Use it in the UI + +Example: + +```tsx + +
diff --git a/src/features/Auth/v1/Pages/SignUpPage.tsx b/src/features/Auth/v1/Pages/SignUpPage.tsx index 3568204..2735cac 100644 --- a/src/features/Auth/v1/Pages/SignUpPage.tsx +++ b/src/features/Auth/v1/Pages/SignUpPage.tsx @@ -4,7 +4,7 @@ import { Link } from "react-router-dom"; import { ArrowLeft, ArrowRight, Loader2 } from "lucide-react"; import { useSignupForm, STEP_FIELDS, SignupFormData } from "../hooks/useSignupForm"; -import { submitCommunitySignup } from "../api/signup"; +import { submitCommunitySignup } from "../utils/signup"; import StepProgress from "../components/signup/StepProgress"; import CommunityStep from "../components/signup/CommunityStep"; import ContactStep from "../components/signup/ContactStep"; diff --git a/src/features/Auth/v1/Store/Auth.Store.ts b/src/features/Auth/v1/Store/Auth.Store.ts new file mode 100644 index 0000000..453db3c --- /dev/null +++ b/src/features/Auth/v1/Store/Auth.Store.ts @@ -0,0 +1,28 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +import { AuthState, User } from "../Types/Auth.type"; + +const useAuthStore = create()( + persist( + (set) => ({ + token: null, + user: null, + + setAuthData: (user: User) => + set({ + user, + }), + + clearAuthData: () => + set({ + user: null, + }), + }), + { + name: "auth-storage", + }, + ), +); + +export default useAuthStore; diff --git a/src/features/Auth/v1/Store/Organization.Store.ts b/src/features/Auth/v1/Store/Organization.Store.ts new file mode 100644 index 0000000..2da8ef9 --- /dev/null +++ b/src/features/Auth/v1/Store/Organization.Store.ts @@ -0,0 +1,24 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { CommunitySchema } from "../Types/Organization.Type"; + + +const useOrganizationStore = create<{ + organization: CommunitySchema | null; + setOrganization: (organization: CommunitySchema) => void; +}>()( + persist( + (set) => ({ + organization: null, + setOrganization: (organization: CommunitySchema) => + set({ + organization, + }), + }), + { + name: "organization-storage", + }, + ), +); + +export default useOrganizationStore; diff --git a/src/features/Auth/v1/Types/Auth.type.ts b/src/features/Auth/v1/Types/Auth.type.ts new file mode 100644 index 0000000..7956fd8 --- /dev/null +++ b/src/features/Auth/v1/Types/Auth.type.ts @@ -0,0 +1,14 @@ +export interface User { + _id: string; + + email: string; + role: string; +} + +export interface AuthState { + // token: string | null; + user: User | null; + + setAuthData: (user: User) => void; + clearAuthData: () => void; +} diff --git a/src/features/Auth/v1/Types/Organization.Type.ts b/src/features/Auth/v1/Types/Organization.Type.ts new file mode 100644 index 0000000..4f99cb5 --- /dev/null +++ b/src/features/Auth/v1/Types/Organization.Type.ts @@ -0,0 +1,20 @@ +export type CommunitySchema = { + CommunityName: string; + Bio: string; + Slug: string; + Website: string; + Country: string; + City: string; + OfficialEmail: string; + ContactPhone: string; + LogoUrl: string; + + SocialLinks: { + github?: string; + discord?: string; + twitter?: string; + linkedin?: string; + youtube?: string; + instagram?: string; + }; +}; diff --git a/src/features/Auth/v1/components/signup/CommunityStep.tsx b/src/features/Auth/v1/components/signup/CommunityStep.tsx index 5426bc4..fe49f11 100644 --- a/src/features/Auth/v1/components/signup/CommunityStep.tsx +++ b/src/features/Auth/v1/components/signup/CommunityStep.tsx @@ -2,7 +2,7 @@ import { useRef, useState } from "react"; import { useFormContext } from "react-hook-form"; import { Upload, X, AlertCircle } from "lucide-react"; import { SignupFormData } from "../../hooks/useSignupForm"; -import { uploadCommunityLogo } from "../../api/signup"; +import { uploadCommunityLogo } from "../../utils/signup"; export default function CommunityStep() { const { diff --git a/src/features/Auth/v1/components/signup/ReviewStep.tsx b/src/features/Auth/v1/components/signup/ReviewStep.tsx index f7bb107..d147301 100644 --- a/src/features/Auth/v1/components/signup/ReviewStep.tsx +++ b/src/features/Auth/v1/components/signup/ReviewStep.tsx @@ -10,7 +10,7 @@ import { Github, Facebook, User, - Lock + Lock, } from "lucide-react"; import { SignupFormData } from "../../hooks/useSignupForm"; diff --git a/src/features/Auth/v1/hooks/useAuth.ts b/src/features/Auth/v1/hooks/useAuth.ts new file mode 100644 index 0000000..76a86c2 --- /dev/null +++ b/src/features/Auth/v1/hooks/useAuth.ts @@ -0,0 +1,89 @@ +import api from "@/utils/axios.utils"; +import { useMutation } from "@tanstack/react-query"; + +import AUTH_ENDPOINTS from "../Constant/Auth.Endpoint.Constant"; +import useAuthStore from "../Store/Auth.Store"; +import useOrganizationStore from "../Store/Organization.Store"; + +const baseUrl = + import.meta.env.VITE_API_BASE_URL || "http://localhost:8000/api/v1"; + +// ========================= +// GET ORGANIZATION +// ========================= + +const useGetOrganizationMutation = () => { + return useMutation({ + mutationKey: ["organization"], + + mutationFn: async (_id: string) => { + const response = await api.get( + `${baseUrl}${AUTH_ENDPOINTS.GET_ORGANIZATION_BY_ID}?ownerId=${_id}` + ); + + return response.data; + }, + + onSuccess: (response) => { + useOrganizationStore.getState().setOrganization(response.data); + }, + + onError: (error) => { + console.error("Failed to fetch organization:", error); + }, + }); +}; + +// ========================= +// LOGIN +// ========================= + +const useLoginMutation = () => { + const organizationMutation = useGetOrganizationMutation(); + + return useMutation({ + mutationKey: ["login"], + + mutationFn: async (credentials: { + email: string; + password: string; + }) => { + const response = await api.post( + `${baseUrl}${AUTH_ENDPOINTS.LOGIN}`, + credentials + ); + + return response.data; + }, + + onSuccess: async (response) => { + const user = response.data; + + console.log("Login successful:", user); + + // Save auth first + useAuthStore.getState().setAuthData(user); + + // Fetch organization if needed + if (user.role === "organization") { + await organizationMutation.mutateAsync(user._id); + } + }, + + onError: (error) => { + console.error("Login failed:", error); + }, + }); +}; + +// ========================= +// AUTH HOOK +// ========================= + +export const useAuth = () => { + const loginMutation = useLoginMutation(); + + return { + loginMutation, + }; +}; \ No newline at end of file diff --git a/src/features/Auth/v1/api/signup.ts b/src/features/Auth/v1/utils/signup.ts similarity index 100% rename from src/features/Auth/v1/api/signup.ts rename to src/features/Auth/v1/utils/signup.ts diff --git a/src/features/Contact_And_Support/v1/Components/InternalSupport_Table.tsx b/src/features/Contact_And_Support/v1/Components/InternalSupport_Table.tsx index cb2f9c5..7e51674 100644 --- a/src/features/Contact_And_Support/v1/Components/InternalSupport_Table.tsx +++ b/src/features/Contact_And_Support/v1/Components/InternalSupport_Table.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { Contact_Permissions, usePermissionMap } from "@/permissions"; import { FiMail, FiCopy, FiCheck } from "react-icons/fi"; type TeamMember = { @@ -94,6 +95,10 @@ const CopyEmailButton = ({ email }: { email: string }) => { }; const InternalSupport_Table = () => { + const { canEmail } = usePermissionMap({ + canEmail: Contact_Permissions.EMAIL_CONTACT, + }); + return (
@@ -102,7 +107,7 @@ const InternalSupport_Table = () => { - + @@ -148,21 +153,24 @@ const InternalSupport_Table = () => { diff --git a/src/features/Contact_And_Support/v1/Components/Support.tsx b/src/features/Contact_And_Support/v1/Components/Support.tsx index cb237bf..dce6506 100644 --- a/src/features/Contact_And_Support/v1/Components/Support.tsx +++ b/src/features/Contact_And_Support/v1/Components/Support.tsx @@ -1,5 +1,6 @@ import DropDown from "@/Component/ui/DropDown"; import Input from "@/Component/ui/Input"; +import { Contact_Permissions, usePermissionMap } from "@/permissions"; import { FormEvent, useState } from "react"; import { MdOutlineSupportAgent } from "react-icons/md"; import { FiAlertCircle, FiCheckCircle } from "react-icons/fi"; @@ -22,6 +23,9 @@ const priorityColor: Record = { }; const Support = () => { + const { canSubmitTicket } = usePermissionMap({ + canSubmitTicket: Contact_Permissions.SUBMIT_SUPPORT_TICKET, + }); const initialCategory = CONTACT_AND_SUPPORT_CONSTANT[0] ?? ""; const [selectedCategory, setSelectedCategory] = useState(initialCategory); const [priority, setPriority] = useState("Medium"); @@ -56,6 +60,7 @@ const Support = () => { const onSubmit = (event: FormEvent) => { event.preventDefault(); + if (!canSubmitTicket) return; if (!validateForm()) { setTicketReference(""); return; @@ -191,11 +196,18 @@ const Support = () => { )}
- - + )} +
@@ -214,6 +226,11 @@ const Support = () => { turnaround time.

+ {!canSubmitTicket && ( +

+ Ticket submission is hidden until support-request permission is granted. +

+ )} ); diff --git a/src/features/Contact_And_Support/v1/Pages/Contact.tsx b/src/features/Contact_And_Support/v1/Pages/Contact.tsx index c1b1f26..c2cddf0 100644 --- a/src/features/Contact_And_Support/v1/Pages/Contact.tsx +++ b/src/features/Contact_And_Support/v1/Pages/Contact.tsx @@ -1,4 +1,5 @@ import ContactHeader from "../Section/ContactHeader"; +import { AccessDenied, Contact_Permissions, PermissionBoundary, PermissionLoading } from "@/permissions"; import InternalDirectoryTable from "../Section/InternalDirectoryTable"; import Support from "../Components/Support"; @@ -6,14 +7,25 @@ const Contact = () => { return (
-
-
- + } + unauthorizedFallback={ + + } + > +
+
+ +
+
+ +
-
- -
-
+
); }; diff --git a/src/features/Events/v1/Components/EventTable.tsx b/src/features/Events/v1/Components/EventTable.tsx index f462d44..8b624db 100644 --- a/src/features/Events/v1/Components/EventTable.tsx +++ b/src/features/Events/v1/Components/EventTable.tsx @@ -1,5 +1,5 @@ -import { MoreVertical } from "lucide-react"; import { useState } from "react"; +import { Event_Permissions, usePermissionMap } from "@/permissions"; import { Event } from "../Event.type"; type EventProps = { @@ -14,11 +14,18 @@ const statusConfig: Record = { }; function EventTable({ events, itemsPerPage }: EventProps) { + const { canView, canEdit, canDelete, canPublish } = usePermissionMap({ + canView: Event_Permissions.VIEW_EVENT, + canEdit: Event_Permissions.UPDATE_EVENT, + canDelete: Event_Permissions.DELETE_EVENT, + canPublish: Event_Permissions.PUBLISH_EVENT, + }); const [currentPage, setCurrentPage] = useState(1); const totalPages = Math.ceil(events.length / itemsPerPage); const indexOfLast = currentPage * itemsPerPage; const indexOfFirst = indexOfLast - itemsPerPage; const currentItems = events.slice(indexOfFirst, indexOfLast); + const canManageActions = canView || canEdit || canDelete || canPublish; return (
Status
- + {canManageActions && } @@ -74,21 +81,26 @@ function EventTable({ events, itemsPerPage }: EventProps) { - + {canManageActions && ( + + )} ); })} diff --git a/src/features/Events/v1/Components/Judge.tsx b/src/features/Events/v1/Components/Judge.tsx index c827e26..87ff92b 100644 --- a/src/features/Events/v1/Components/Judge.tsx +++ b/src/features/Events/v1/Components/Judge.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from "react"; import Input from "@/Component/ui/Input"; +import { Event_Permissions, PermissionGate } from "@/permissions"; import { CiSearch } from "react-icons/ci"; import { IoMdAdd } from "react-icons/io"; import JudgeCard from "./JudgeCard"; @@ -53,9 +54,11 @@ const Judge = ({ isExpanded = true, onToggleExpand }: JudgeProps) => {
- + + + {onToggleExpand && (
- + + + {onToggleExpand && (
- + + + {onToggleExpand && (
- + + + {onToggleExpand && (
); diff --git a/src/features/Events/v1/Sections/Event_View_Header.tsx b/src/features/Events/v1/Sections/Event_View_Header.tsx index 09831a7..ee36eb3 100644 --- a/src/features/Events/v1/Sections/Event_View_Header.tsx +++ b/src/features/Events/v1/Sections/Event_View_Header.tsx @@ -1,4 +1,5 @@ import Button from "@/Component/ui/Button"; +import { Event_Permissions, PermissionGate } from "@/permissions"; import { useCallback, useState } from "react"; import { IoMdAdd } from "react-icons/io"; import { useNavigate } from "react-router-dom"; @@ -32,13 +33,15 @@ const Event_View_Header = () => { Manage all your events in one place. Create, edit, and track event details with ease.

-
-
+ +
+
+
diff --git a/src/features/Events/v1/Sections/Settings.tsx b/src/features/Events/v1/Sections/Settings.tsx index ba9facf..b4e446f 100644 --- a/src/features/Events/v1/Sections/Settings.tsx +++ b/src/features/Events/v1/Sections/Settings.tsx @@ -1,10 +1,8 @@ - import { IoSettingsSharp } from "react-icons/io5"; import EventSetting from "../Components/EventSetting"; import { theme } from "@/theme"; const Settings = () => { - return (
{
-
- + {canManageMembers && } @@ -55,21 +61,18 @@ const MemberTable = ({ members }: MemberTableProps) => { {member.certificates} - + {canManageMembers && ( + + )} ))} diff --git a/src/features/Member/v1/Pages/MemberPage.tsx b/src/features/Member/v1/Pages/MemberPage.tsx index 8ba6956..33c165c 100644 --- a/src/features/Member/v1/Pages/MemberPage.tsx +++ b/src/features/Member/v1/Pages/MemberPage.tsx @@ -1,4 +1,5 @@ import MemberHeader from "../Components/MemberHeader"; +import { AccessDenied, Member_Permissions, PermissionBoundary, PermissionLoading } from "@/permissions"; import MemberTable from "../Components/MemberTable"; import SearchMember from "../Components/SearchMember"; @@ -41,8 +42,19 @@ const MemberPage = () => { return (
- - + } + unauthorizedFallback={ + + } + > + + +
); }; diff --git a/src/features/Projects/Pages/ProjectsPage.tsx b/src/features/Projects/Pages/ProjectsPage.tsx index 3f40849..26f4f2d 100644 --- a/src/features/Projects/Pages/ProjectsPage.tsx +++ b/src/features/Projects/Pages/ProjectsPage.tsx @@ -1,15 +1,27 @@ import PagePlaceholder from "@/features/Member/v1/Components/PagePlaceholder"; +import { AccessDenied, PermissionBoundary, PermissionLoading, Project_Permissions } from "@/permissions"; const ProjectsPage = () => { return ( -
-
- } + unauthorizedFallback={ + + } + > +
+
+ +
-
+ ); }; diff --git a/src/features/SideBar/v1/Section/SideBar.tsx b/src/features/SideBar/v1/Section/SideBar.tsx index a80cf95..bfbd147 100644 --- a/src/features/SideBar/v1/Section/SideBar.tsx +++ b/src/features/SideBar/v1/Section/SideBar.tsx @@ -1,12 +1,28 @@ import { RiContactsBookFill } from "react-icons/ri"; -import { MdAssignment, MdDashboard, MdEvent, MdGroup, MdSettings, MdWork, MdWebhook } from "react-icons/md"; +import { + MdAssignment, + MdDashboard, + MdEvent, + MdGroup, + MdSettings, + MdWork, + MdWebhook, +} from "react-icons/md"; import { useTheme } from "@/theme"; import { ThemeToggle } from "@/Component/ui/ThemeToggle"; import SideBarLink from "../Components/SideBarLink"; -import { dashboardData } from "@/features/Member/v1/mock/dashboardData"; + +import useAuthStore from "@/features/Auth/v1/Store/Auth.Store"; + +import useOrganizationStore from "@/features/Auth/v1/Store/Organization.Store"; const SideBar = () => { + const user = useAuthStore((state) => state.user); + const organization = useOrganizationStore((state) => state.organization); + + console.log("User in SideBar:", user); + console.log("Organization in SideBar:", organization); const { theme } = useTheme(); return ( @@ -67,16 +83,16 @@ const SideBar = () => { style={{ backgroundColor: theme.bg.surfaceSecondary }} > Profile

- {dashboardData.user.name} + {organization?.CommunityName}

- {dashboardData.user.role} + {user?.role}

diff --git a/src/features/Tasks/v1/Task.types.ts b/src/features/Tasks/v1/Task.types.ts index 4b0ee5f..2c25f92 100644 --- a/src/features/Tasks/v1/Task.types.ts +++ b/src/features/Tasks/v1/Task.types.ts @@ -120,4 +120,4 @@ export interface ReviewSubmissionPayload { decision: ReviewDecision; score?: number; feedback?: string; -} \ No newline at end of file +} diff --git a/src/features/Tasks/v1/components/common/Avatar.tsx b/src/features/Tasks/v1/components/common/Avatar.tsx index 6656584..c9ac099 100644 --- a/src/features/Tasks/v1/components/common/Avatar.tsx +++ b/src/features/Tasks/v1/components/common/Avatar.tsx @@ -13,9 +13,15 @@ interface AvatarProps { // Color map for initials fallback — derived from name char code const BG_COLORS = [ - "bg-indigo-500", "bg-pink-500", "bg-orange-500", - "bg-emerald-500", "bg-sky-500", "bg-violet-500", - "bg-rose-500", "bg-amber-500","bg-teal-500", + "bg-indigo-500", + "bg-pink-500", + "bg-orange-500", + "bg-emerald-500", + "bg-sky-500", + "bg-violet-500", + "bg-rose-500", + "bg-amber-500", + "bg-teal-500", ]; function getColor(name: string): string { @@ -24,31 +30,47 @@ function getColor(name: string): string { } function getInitials(name: string): string { - return name.split(" ").map(w => w[0]).join("").slice(0, 2).toUpperCase(); + return name + .split(" ") + .map((w) => w[0]) + .join("") + .slice(0, 2) + .toUpperCase(); } const SIZE_MAP = { - xs: { outer: "w-6 h-6", text: "text-[9px]", dot: "w-1.5 h-1.5 -right-0 -bottom-0", ring: "ring-1" }, - sm: { outer: "w-7 h-7", text: "text-[10px]", dot: "w-2 h-2 -right-0 -bottom-0", ring: "ring-2" }, - md: { outer: "w-9 h-9", text: "text-xs", dot: "w-2.5 h-2.5 right-0 bottom-0", ring: "ring-2" }, - lg: { outer: "w-12 h-12", text: "text-sm", dot: "w-3 h-3 right-0.5 bottom-0.5", ring: "ring-2" }, + xs: { + outer: "w-6 h-6", + text: "text-[9px]", + dot: "w-1.5 h-1.5 -right-0 -bottom-0", + ring: "ring-1", + }, + sm: { outer: "w-7 h-7", text: "text-[10px]", dot: "w-2 h-2 -right-0 -bottom-0", ring: "ring-2" }, + md: { outer: "w-9 h-9", text: "text-xs", dot: "w-2.5 h-2.5 right-0 bottom-0", ring: "ring-2" }, + lg: { outer: "w-12 h-12", text: "text-sm", dot: "w-3 h-3 right-0.5 bottom-0.5", ring: "ring-2" }, }; const STATUS_COLOR = { - online: "bg-emerald-400", + online: "bg-emerald-400", offline: "bg-gray-300", - busy: "bg-red-400", + busy: "bg-red-400", }; export default function Avatar({ - name, src, role, size = "sm", - showTooltip = true, status = null, ring = false, ringColor = "ring-[var(--cd-surface)]", + name, + src, + role, + size = "sm", + showTooltip = true, + status = null, + ring = false, + ringColor = "ring-[var(--cd-surface)]", }: AvatarProps) { const [imgError, setImgError] = useState(false); - const [hovered, setHovered] = useState(false); + const [hovered, setHovered] = useState(false); const sz = SIZE_MAP[size]; const initials = getInitials(name); - const bgColor = getColor(name); + const bgColor = getColor(name); return (
setHovered(false)} > {/* Avatar circle */} -
+ `} + > {!imgError ? ( ) : ( /* Initials fallback */ -
+
{initials}
)} @@ -111,7 +137,7 @@ interface AvatarGroupProps { } export function AvatarGroup({ members, max = 3, size = "sm" }: AvatarGroupProps) { - const visible = members.slice(0, max); + const visible = members.slice(0, max); const overflow = members.length - max; const sz = SIZE_MAP[size]; @@ -137,7 +163,9 @@ export function AvatarGroup({ members, max = 3, size = "sm" }: AvatarGroupProps) borderColor: "var(--cd-surface)", }} > - +{overflow} + + +{overflow} +
)}
diff --git a/src/features/Tasks/v1/components/common/CommentsSection.tsx b/src/features/Tasks/v1/components/common/CommentsSection.tsx index 5df2bb0..5eccfd0 100644 --- a/src/features/Tasks/v1/components/common/CommentsSection.tsx +++ b/src/features/Tasks/v1/components/common/CommentsSection.tsx @@ -3,7 +3,9 @@ import { formatDistanceToNow, parseISO } from "date-fns"; import { Loader2, MessageCircle, Send } from "lucide-react"; import { useComments, useAddComment } from "../../hooks/useComments"; -interface Props { taskId: string; } +interface Props { + taskId: string; +} export default function CommentsSection({ taskId }: Props) { const { data: comments = [], isLoading } = useComments(taskId); @@ -12,7 +14,10 @@ export default function CommentsSection({ taskId }: Props) { const [error, setError] = useState(""); const handleSubmit = async () => { - if (!text.trim()) { setError("Comment cannot be empty."); return; } + if (!text.trim()) { + setError("Comment cannot be empty."); + return; + } setError(""); await addComment.mutateAsync({ taskId, text: text.trim() }); setText(""); @@ -34,13 +39,19 @@ export default function CommentsSection({ taskId }: Props) {

Loading comments…

) : comments.length === 0 ? (
-

No comments yet. Be the first to start the conversation!

+

+ No comments yet. Be the first to start the conversation! +

) : (
{comments.map((c) => (
- {c.author} + {c.author}
{c.author} @@ -60,9 +71,15 @@ export default function CommentsSection({ taskId }: Props) {
Team Member Role / Department Email AddressActions{canEmail ? "Actions" : "Quick Copy"}
Teams SubmissionsActionsActions
{event.teams} {event.submissions} - - +
+ {canView && ( + + )} + {canEdit && ( + + )} + {canPublish && event.status !== "Completed" && ( + + )} + {canDelete && ( + + )} +
+
Status Skills CertificatesActionsActions
- - +
+ {canEdit && ( + + )} + {canDelete && ( + + )} +
+