From 767de71ad48ef91159e38ed998a1bb7ca704ba04 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 00:30:30 +0530 Subject: [PATCH 01/13] [Fix]: Major fiixed the CSRF Token issue in production mode --- frontend/src/types/Common.ts | 5 +++++ frontend/src/utils/quickdo-category.ts | 1 + frontend/src/utils/quickdo-dashboard.ts | 1 + frontend/src/utils/quickdo.ts | 3 ++- frontend/src/utils/sign-up.ts | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/src/types/Common.ts b/frontend/src/types/Common.ts index 41cac96..0856bfc 100644 --- a/frontend/src/types/Common.ts +++ b/frontend/src/types/Common.ts @@ -1,6 +1,11 @@ import { IconType } from "react-icons"; // ! COMMON +declare global { + interface Window { + csrf_token: string; + } +} export type Status = 'Open' | 'Completed' | 'Cancelled'; // export type Priority = 'Low' | 'Medium' | 'High'; diff --git a/frontend/src/utils/quickdo-category.ts b/frontend/src/utils/quickdo-category.ts index 80820c8..a11e54a 100644 --- a/frontend/src/utils/quickdo-category.ts +++ b/frontend/src/utils/quickdo-category.ts @@ -7,6 +7,7 @@ const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN || null; // ? GENERATE THE HEADERS FOR AUTHORIZATION ONCE const getHeaders = () => ({ Authorization: AUTH_TOKEN, // ? ADD AUTHORIZATION HEADER + "X-Frappe-CSRF-Token": window.csrf_token, // ?ADD CSRF TOKEN FROM WINDOW OBJECT }); // ? CATEGORY SERVICE API FUNCTION diff --git a/frontend/src/utils/quickdo-dashboard.ts b/frontend/src/utils/quickdo-dashboard.ts index 5e6e3ab..c80ec16 100644 --- a/frontend/src/utils/quickdo-dashboard.ts +++ b/frontend/src/utils/quickdo-dashboard.ts @@ -7,6 +7,7 @@ const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN || null; // ? GENERATE THE HEADERS FOR AUTHORIZATION ONCE const getHeaders = () => ({ Authorization: AUTH_TOKEN, // ? ADD AUTHORIZATION HEADER + "X-Frappe-CSRF-Token": window.csrf_token, // ?ADD CSRF TOKEN FROM WINDOW OBJECT }); // ? FETCH DASHBOARD DATA WITH OPTIONAL FILTERS diff --git a/frontend/src/utils/quickdo.ts b/frontend/src/utils/quickdo.ts index da49e0d..7a1e8e0 100644 --- a/frontend/src/utils/quickdo.ts +++ b/frontend/src/utils/quickdo.ts @@ -6,7 +6,8 @@ const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN || null; // ? GENERATE THE HEADERS FOR AUTHORIZATION ONCE const getHeaders = () => ({ - Authorization: AUTH_TOKEN, // ? ADD AUTHORIZATION HEADER + Authorization: AUTH_TOKEN, // ? ADD AUTHORIZATION HEADER, + "X-Frappe-CSRF-Token": window.csrf_token, // ?ADD CSRF TOKEN FROM WINDOW OBJECT }); // ? FETCH ALL QUICKDOS WITH OPTIONAL FILTERS AND SORTING diff --git a/frontend/src/utils/sign-up.ts b/frontend/src/utils/sign-up.ts index ae2f560..1f648e0 100644 --- a/frontend/src/utils/sign-up.ts +++ b/frontend/src/utils/sign-up.ts @@ -7,6 +7,7 @@ const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN || null; // ? GENERATE THE HEADERS FOR AUTHORIZATION ONCE const getHeaders = () => ({ Authorization: AUTH_TOKEN, // ? ADD AUTHORIZATION HEADER + "X-Frappe-CSRF-Token": window.csrf_token, // ?ADD CSRF TOKEN FROM WINDOW OBJECT }); // ? SIGN UP From 92cbe13687d544143bf2d03aee22ba5f3f935c46 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 00:32:58 +0530 Subject: [PATCH 02/13] [Chore]: Added git CI workflow --- .github/workflows/ci.yml | 107 +++++++++++++++++++++++++++++++++++ .github/workflows/linter.yml | 60 ++++++++++++++++++++ .pre-commit-config.yaml | 18 +++--- 3 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/linter.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..730016f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,107 @@ +name: CI + +on: + push: + branches: + - develop + pull_request: + +concurrency: + group: develop-quickdo-${{ github.event.number }} + cancel-in-progress: true + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + name: Server + + services: + redis-cache: + image: redis:alpine + ports: + - 13000:6379 + redis-queue: + image: redis:alpine + ports: + - 11000:6379 + mariadb: + image: mariadb:10.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Find tests + run: | + echo "Finding tests" + grep -rn "def test" > /dev/null + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18 + check-latest: true + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: 'echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT' + + - uses: actions/cache@v4 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install MariaDB Client + run: | + sudo apt update + sudo apt-get install mariadb-client + + - name: Setup + run: | + pip install frappe-bench + bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench + mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" + mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" + + - name: Install + working-directory: /home/runner/frappe-bench + run: | + bench get-app quickdo $GITHUB_WORKSPACE + bench setup requirements --dev + bench new-site --db-root-password root --admin-password admin test_site + bench --site test_site install-app quickdo + bench build + env: + CI: 'Yes' + + - name: Run Tests + working-directory: /home/runner/frappe-bench + run: | + bench --site test_site set-config allow_tests true + bench --site test_site run-tests --app quickdo + env: + TYPE: server \ No newline at end of file diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..94488c1 --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,60 @@ +name: Linters + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + linter: + name: 'Frappe Linter' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + cache: pip + - uses: pre-commit/action@v3.0.0 + + - name: Download Semgrep rules + run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules + + - name: Run Semgrep rules + run: | + pip install semgrep + semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness + + deps-vulnerable-check: + name: 'Vulnerable Dependency Check' + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Install and run pip-audit + run: | + pip install pip-audit + cd ${GITHUB_WORKSPACE} + pip-audit --desc on . \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d58ce6f..dff099d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,15 @@ exclude: 'node_modules|.git' -default_stages: [commit] +default_stages: [pre-commit] fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v5.0.0 hooks: - id: trailing-whitespace files: "quickdo.*" exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" - - id: check-yaml - id: check-merge-conflict - id: check-ast - id: check-json @@ -19,14 +18,17 @@ repos: - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.8.1 hooks: - id: ruff - name: "Run ruff linter and apply fixes" - args: ["--fix"] + name: "Run ruff import sorter" + args: ["--select=I", "--fix"] + + - id: ruff + name: "Run ruff linter" - id: ruff-format - name: "Format Python code" + name: "Run ruff formatter" - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.7.1 @@ -64,4 +66,4 @@ repos: ci: autoupdate_schedule: weekly skip: [] - submodules: false + submodules: false \ No newline at end of file From aea44d552fee82af213b52e89dca208e505e3474 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 00:42:53 +0530 Subject: [PATCH 03/13] [Fix]: Eslint fix 1 --- quickdo/quickdo/doctype/quickdo/quickdo_calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js b/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js index 807e594..7b40830 100644 --- a/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js +++ b/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js @@ -12,4 +12,4 @@ frappe.views.calendar["QuickDo"] = { color: "color", filters: [], get_events_method: "frappe.desk.calendar.get_events", -}; \ No newline at end of file +}; From cc8c9f0c7a60301fc012a246442bfd8b5553caa2 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 00:56:43 +0530 Subject: [PATCH 04/13] [Fix]: ESlint 2 --- frontend/eslint.config.js | 49 +- frontend/postcss.config.js | 10 +- frontend/tailwind.config.js | 29 +- quickdo/api.py | 1251 ++++++++--------- quickdo/hooks.py | 16 +- .../doctype/quickdo/quickdo_calendar.js | 26 +- 6 files changed, 681 insertions(+), 700 deletions(-) diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 092408a..20b1f8c 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,28 +1,25 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ['dist'] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, - }, -) + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, + } +); diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 2e7af2b..7b75c83 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -1,6 +1,6 @@ export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 6182034..560f3e4 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,17 +1,16 @@ /** @type {import('tailwindcss').Config} */ export default { - darkMode: ["class"], - content: ["./src/**/*.{html,jsx,tsx,vue,js,ts}"], - theme: { - extend: { - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - colors: {} - } - }, - plugins: [require("tailwindcss-animate")], -} - + darkMode: ["class"], + content: ["./src/**/*.{html,jsx,tsx,vue,js,ts}"], + theme: { + extend: { + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + colors: {}, + }, + }, + plugins: [require("tailwindcss-animate")], +}; diff --git a/quickdo/api.py b/quickdo/api.py index 07d940d..1148d92 100644 --- a/quickdo/api.py +++ b/quickdo/api.py @@ -1,245 +1,238 @@ +from datetime import datetime, timedelta + import frappe from frappe.core.doctype.user.user import generate_keys # import frappe.utils from frappe.utils import add_days, add_months from frappe.utils.dateutils import getdate -from datetime import datetime, timedelta #! api/method/quickdo.api.get_todo_with_categories # ? QUICKDO LIST API WITH CATEGORIES @frappe.whitelist() def get_quickdo_with_categories( - doctype, - filters=None, - fields=None, - order_by=None, + doctype, + filters=None, + fields=None, + order_by=None, ): - # ? FINAL DATA - final_data = [] - - try: - # ? PARENT QUICKDO LIST - quickdo_list = frappe.get_list( - doctype=doctype, - filters=frappe.parse_json(filters) if filters else {}, - fields=frappe.parse_json(fields) if fields else ["name"], - order_by=order_by, - ) - - # ? CHILD CATEGORY LIST - categories_list = frappe.get_all( - "QuickDo Categories", - filters={}, - fields=["category", "parent"], - ) - - # ? HASH MAP FOR PARENT CHILD MAPPING - hash_map = {} - - # ? Map categories directly to their parent - for category in categories_list: - if category.parent not in hash_map: - hash_map[category.parent] = {"categories": []} - hash_map[category.parent]["categories"].append( - {"category": category.category} - ) - - # ? MAP THE CHILD RECORDS WITH PARENTS - for quickdo in quickdo_list: - quickdo_data = quickdo - quickdo_data["categories"] = hash_map.get(quickdo.name, {}).get( - "categories", [] - ) - final_data.append(quickdo_data) - - except Exception as e: - return f"Unexpected Error: {str(e)}" - - else: - return final_data + # ? FINAL DATA + final_data = [] + + try: + # ? PARENT QUICKDO LIST + quickdo_list = frappe.get_list( + doctype=doctype, + filters=frappe.parse_json(filters) if filters else {}, + fields=frappe.parse_json(fields) if fields else ["name"], + order_by=order_by, + ) + + # ? CHILD CATEGORY LIST + categories_list = frappe.get_all( + "QuickDo Categories", + filters={}, + fields=["category", "parent"], + ) + + # ? HASH MAP FOR PARENT CHILD MAPPING + hash_map = {} + + # ? Map categories directly to their parent + for category in categories_list: + if category.parent not in hash_map: + hash_map[category.parent] = {"categories": []} + hash_map[category.parent]["categories"].append({"category": category.category}) + + # ? MAP THE CHILD RECORDS WITH PARENTS + for quickdo in quickdo_list: + quickdo_data = quickdo + quickdo_data["categories"] = hash_map.get(quickdo.name, {}).get("categories", []) + final_data.append(quickdo_data) + + except Exception as e: + return f"Unexpected Error: {str(e)}" + + else: + return final_data #! api/method/quickdo.api.get_quickdo_calendar_data # ? QUICKDO CALENDAR DATA @frappe.whitelist() def get_quickdo_calendar_data( - filters=None, + filters=None, ): - - try: - # ? QUICKDO LIST - quickdo_list = frappe.get_list( - doctype="QuickDo", - filters=frappe.parse_json(filters), - fields=[ - "name", - "date", - "creation", - "description", - "color", - "is_important", - "send_reminder", - ], - # order_by="modified asc", - ) - - # ? MAP QUICKDO WITH THE NEW FIELDS - for quickdo in quickdo_list: - - # ? IF THE DATE IS NOT SET MAKE CREATION AS END DATE - if not quickdo.get("date"): - quickdo["date"] = getdate(quickdo.get("creation")) - - # ? MAP FIELDS - quickdo["title"] = quickdo.get("description") - quickdo["start"] = quickdo.get("date") - quickdo["end"] = quickdo.get("date") - - # ? REMOVE UNUSED FIELDS - quickdo.pop("creation", None) - quickdo.pop("description", None) - quickdo.pop("date", None) - - except Exception as e: - return f"Unexpected Error: {str(e)}" - - else: - return quickdo_list + try: + # ? QUICKDO LIST + quickdo_list = frappe.get_list( + doctype="QuickDo", + filters=frappe.parse_json(filters), + fields=[ + "name", + "date", + "creation", + "description", + "color", + "is_important", + "send_reminder", + ], + # order_by="modified asc", + ) + + # ? MAP QUICKDO WITH THE NEW FIELDS + for quickdo in quickdo_list: + # ? IF THE DATE IS NOT SET MAKE CREATION AS END DATE + if not quickdo.get("date"): + quickdo["date"] = getdate(quickdo.get("creation")) + + # ? MAP FIELDS + quickdo["title"] = quickdo.get("description") + quickdo["start"] = quickdo.get("date") + quickdo["end"] = quickdo.get("date") + + # ? REMOVE UNUSED FIELDS + quickdo.pop("creation", None) + quickdo.pop("description", None) + quickdo.pop("date", None) + + except Exception as e: + return f"Unexpected Error: {str(e)}" + + else: + return quickdo_list #! api/method/quickdo.api.get_total_upcoming_quickdo # ? TOTAL UPCOMING QUICKDO @frappe.whitelist() def get_total_upcoming_quickdo(): - try: - quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "status": "Open", - }, - fields=["name"], - ) - quickdo_count = len(quickdo_list) - - except Exception as e: - frappe.log_error("Error While Getting Total Upcoming QuickDos", str(e)) - return { - "success": False, - "message": "Error While Getting Total Upcoming QuickDos!", - "data": None, - } - - else: - return { - "success": True, - "message": "Total Upcoming QuickDos Are Loaded Successfully!", - "data": { - "count": quickdo_count, - }, - } + try: + quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "status": "Open", + }, + fields=["name"], + ) + quickdo_count = len(quickdo_list) + + except Exception as e: + frappe.log_error("Error While Getting Total Upcoming QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Total Upcoming QuickDos!", + "data": None, + } + + else: + return { + "success": True, + "message": "Total Upcoming QuickDos Are Loaded Successfully!", + "data": { + "count": quickdo_count, + }, + } #! api/method/quickdo.api.get_upcoming_important_quickdo # ? UPCOMING IMPORTANT QUICKDO @frappe.whitelist() def get_upcoming_important_quickdo(): - try: - quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "status": "Open", - "is_important": 1, - }, - fields=["name"], - ) - quickdo_count = len(quickdo_list) - - except Exception as e: - frappe.log_error( - "Error While Getting Total Upcoming Important QuickDos", str(e) - ) - return { - "success": False, - "message": "Error While Getting Upcoming Important QuickDos!", - "data": None, - } - - else: - return { - "success": True, - "message": "Upcoming Important QuickDos Are Loaded Successfully!", - "data": { - "count": quickdo_count, - }, - } + try: + quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "status": "Open", + "is_important": 1, + }, + fields=["name"], + ) + quickdo_count = len(quickdo_list) + + except Exception as e: + frappe.log_error("Error While Getting Total Upcoming Important QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Upcoming Important QuickDos!", + "data": None, + } + + else: + return { + "success": True, + "message": "Upcoming Important QuickDos Are Loaded Successfully!", + "data": { + "count": quickdo_count, + }, + } #! api/method/quickdo.api.get_overdue_quickdo # ? OVERDUE QUICKDO @frappe.whitelist() def get_overdue_quickdo(): - try: - current_date = getdate(datetime.today()) - quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "status": "Open", - "date": ["<", current_date], - }, - fields=["name"], - ) - quickdo_count = len(quickdo_list) - - except Exception as e: - frappe.log_error("Error While Getting Total Overdue QuickDos", str(e)) - return { - "success": False, - "message": "Error While Getting Overdue QuickDos!", - "data": None, - } - - else: - return { - "success": True, - "message": "Overdue QuickDos Are Loaded Successfully!", - "data": { - "count": quickdo_count, - }, - } + try: + current_date = getdate(datetime.today()) + quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "status": "Open", + "date": ["<", current_date], + }, + fields=["name"], + ) + quickdo_count = len(quickdo_list) + + except Exception as e: + frappe.log_error("Error While Getting Total Overdue QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Overdue QuickDos!", + "data": None, + } + + else: + return { + "success": True, + "message": "Overdue QuickDos Are Loaded Successfully!", + "data": { + "count": quickdo_count, + }, + } #! api/method/quickdo.api.get_total_completed_quickdo # ? TOTAL COMPLETED QUICKDO @frappe.whitelist() def get_total_completed_quickdo(): - try: - quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "status": "Completed", - }, - fields=["name"], - ) - quickdo_count = len(quickdo_list) - - except Exception as e: - frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) - return { - "success": False, - "message": "Error While Getting Total Completed QuickDos!", - "data": None, - } - - else: - return { - "success": True, - "message": "Total Completed QuickDos Are Loaded Successfully!", - "data": { - "count": quickdo_count, - }, - } + try: + quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "status": "Completed", + }, + fields=["name"], + ) + quickdo_count = len(quickdo_list) + + except Exception as e: + frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Total Completed QuickDos!", + "data": None, + } + + else: + return { + "success": True, + "message": "Total Completed QuickDos Are Loaded Successfully!", + "data": { + "count": quickdo_count, + }, + } #! api/method/quickdo.api.get_quickdo_category_wise @@ -247,470 +240,462 @@ def get_total_completed_quickdo(): # ? QUICKDO CATEGORY WISE @frappe.whitelist() def get_quickdo_category_wise(): - try: - - # ? GET THE CATEGORY LIST - quickdo_category_list = frappe.get_all( - "QuickDo Category", - fields=["name", "category"], - order_by="category asc", - ) - - # ? GET QUICKDO LIST - quickdo_data_list = frappe.get_all( - "QuickDo Categories", - fields=["category"], - order_by="category asc", - ) - - # ? CREATE A HASH MAP FOR MAPPING DATA - hash_map = { - category.get("category"): sum( - 1 - for quickdo in quickdo_data_list - if quickdo.get("category") == category.get("category") - ) - for category in quickdo_category_list - } - - # ? GET CATEGORY WISE QUICKDO COUNT - quickdo_count = [ - {"category": key, "quickdo": value} for key, value in hash_map.items() - ] - - # ? SORT QUICKDO COUNT WISE HIGH TO LOW - quickdo_count = sorted(quickdo_count, key=lambda x: x["quickdo"], reverse=True) - - except Exception as e: - frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) - return { - "success": False, - "message": "Error While Getting Total Completed QuickDos!", - "data": None, - } - - return { - "success": True, - "message": "Total Completed QuickDos Are Loaded Successfully!", - "data": quickdo_count, - } + try: + # ? GET THE CATEGORY LIST + quickdo_category_list = frappe.get_all( + "QuickDo Category", + fields=["name", "category"], + order_by="category asc", + ) + + # ? GET QUICKDO LIST + quickdo_data_list = frappe.get_all( + "QuickDo Categories", + fields=["category"], + order_by="category asc", + ) + + # ? CREATE A HASH MAP FOR MAPPING DATA + hash_map = { + category.get("category"): sum( + 1 for quickdo in quickdo_data_list if quickdo.get("category") == category.get("category") + ) + for category in quickdo_category_list + } + + # ? GET CATEGORY WISE QUICKDO COUNT + quickdo_count = [{"category": key, "quickdo": value} for key, value in hash_map.items()] + + # ? SORT QUICKDO COUNT WISE HIGH TO LOW + quickdo_count = sorted(quickdo_count, key=lambda x: x["quickdo"], reverse=True) + + except Exception as e: + frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Total Completed QuickDos!", + "data": None, + } + + return { + "success": True, + "message": "Total Completed QuickDos Are Loaded Successfully!", + "data": quickdo_count, + } #! api/method/quickdo.api.get_status_wise_quickdo # ? STATUS WISE QUICKDO @frappe.whitelist() def get_status_wise_quickdo(): - try: - - # ? OPEN QUICKDO LIST - open_quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "status": "Open", - }, - fields=["name"], - ) - - # ? OPEN QUICKDO COUNT - open_quickdo_count = len(open_quickdo_list) - - # ? CANCELLED QUICKDO LIST - cancelled = frappe.get_list( - "QuickDo", - filters={ - "status": "Cancelled", - }, - fields=["name"], - ) - - # ? CANCELLED QUICKDO COUNT - cancelled_quickdo_count = len(cancelled) - - # ? COMPLETED QUICKDO LIST - completed_quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "status": "Completed", - }, - fields=["name"], - ) - - # ? COMPLETED QUICKDO COUNT - completed_quickdo_count = len(completed_quickdo_list) - - # ? MERGE DATASET - final_data = [ - {"status": "Open", "quickdo": open_quickdo_count, "fill": "#3c50e0"}, - { - "status": "Completed", - "quickdo": completed_quickdo_count, - "fill": "#29CD42", - }, - { - "status": "Cancelled", - "quickdo": cancelled_quickdo_count, - "fill": "#CB2929", - }, - ] - except Exception as e: - frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) - return { - "success": False, - "message": "Error While Getting Total Completed QuickDos!", - "data": None, - } - - else: - return { - "success": True, - "message": "Total Completed QuickDos Are Loaded Successfully!", - "data": final_data, - } + try: + # ? OPEN QUICKDO LIST + open_quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "status": "Open", + }, + fields=["name"], + ) + + # ? OPEN QUICKDO COUNT + open_quickdo_count = len(open_quickdo_list) + + # ? CANCELLED QUICKDO LIST + cancelled = frappe.get_list( + "QuickDo", + filters={ + "status": "Cancelled", + }, + fields=["name"], + ) + + # ? CANCELLED QUICKDO COUNT + cancelled_quickdo_count = len(cancelled) + + # ? COMPLETED QUICKDO LIST + completed_quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "status": "Completed", + }, + fields=["name"], + ) + + # ? COMPLETED QUICKDO COUNT + completed_quickdo_count = len(completed_quickdo_list) + + # ? MERGE DATASET + final_data = [ + {"status": "Open", "quickdo": open_quickdo_count, "fill": "#3c50e0"}, + { + "status": "Completed", + "quickdo": completed_quickdo_count, + "fill": "#29CD42", + }, + { + "status": "Cancelled", + "quickdo": cancelled_quickdo_count, + "fill": "#CB2929", + }, + ] + except Exception as e: + frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Total Completed QuickDos!", + "data": None, + } + + else: + return { + "success": True, + "message": "Total Completed QuickDos Are Loaded Successfully!", + "data": final_data, + } #! api/method/quickdo.api.get_important_wise_quickdo # ? IMPORTANCE WISE QUICKDO @frappe.whitelist() def get_important_wise_quickdo(): - try: - - # ? TOTAL QUICKDO LIST - total_quickdo_list = frappe.get_list( - "QuickDo", - filters={}, - fields=["name"], - ) - - # ? TOTAL QUICKDO COUNT - total_quickdo_count = len(total_quickdo_list) - - # ? IMPORTANT QUICKDO LIST - important_quickdo_list = frappe.get_list( - "QuickDo", - filters={ - "is_important": 1, - }, - fields=["name"], - ) - - # ? IMPORTANT QUICKDO COUNT - important_quickdo_count = len(important_quickdo_list) - - # ? MERGE DATASET - final_data = [ - { - "important": important_quickdo_count, - "total": total_quickdo_count, - "fill": "#3c50e0", - } - ] - - except Exception as e: - frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) - return { - "success": False, - "message": "Error While Getting Total Completed QuickDos!", - "data": None, - } - - else: - return { - "success": True, - "message": "Total Completed QuickDos Are Loaded Successfully!", - "data": final_data, - } + try: + # ? TOTAL QUICKDO LIST + total_quickdo_list = frappe.get_list( + "QuickDo", + filters={}, + fields=["name"], + ) + + # ? TOTAL QUICKDO COUNT + total_quickdo_count = len(total_quickdo_list) + + # ? IMPORTANT QUICKDO LIST + important_quickdo_list = frappe.get_list( + "QuickDo", + filters={ + "is_important": 1, + }, + fields=["name"], + ) + + # ? IMPORTANT QUICKDO COUNT + important_quickdo_count = len(important_quickdo_list) + + # ? MERGE DATASET + final_data = [ + { + "important": important_quickdo_count, + "total": total_quickdo_count, + "fill": "#3c50e0", + } + ] + + except Exception as e: + frappe.log_error("Error While Getting Total Completed QuickDos", str(e)) + return { + "success": False, + "message": "Error While Getting Total Completed QuickDos!", + "data": None, + } + + else: + return { + "success": True, + "message": "Total Completed QuickDos Are Loaded Successfully!", + "data": final_data, + } # ! api/method/quickdo.api.quickdo_creation_trend_over_time # ? QUICKDO CREATION TREND OVER TIME @frappe.whitelist() def quickdo_creation_trend_over_time( - frequency="monthly", - month=None, - year=None, + frequency="monthly", + month=None, + year=None, ): - # DEFINE A DICTIONARY FOR MONTHS - months = { - 1: "January", - 2: "February", - 3: "March", - 4: "April", - 5: "May", - 6: "June", - 7: "July", - 8: "August", - 9: "September", - 10: "October", - 11: "November", - 12: "December", - } - try: - # ? GET TODAY'S DATE AND SET DEFAULT MONTH AND YEAR - today = getdate(datetime.today()) - month = month or today.month - year = year or today.year - - # ? INITIALIZE VARIABLES FOR CHART DATA - data = [] - - # ? FREQUENCY BASED CONDITIONS: DAILY, WEEKLY, OR MONTHLY - # ? LOGIC FOR DAILY FREQUENCY - if frequency == "daily": - # CALCULATE DAYS IN THE MONTH - start_date = getdate(f"{year}-{month}-01") - days_in_month = (add_months(start_date, 1) - start_date).days - - for day in range(1, days_in_month + 1): - day_label = f"{str(day).zfill(2)}-{str(month).zfill(2)}-{year}" - count_data = frappe.db.get_list( - "QuickDo", - filters=[ - ["creation", "=", getdate(f"{year}-{month}-{day}")], - ], - fields=["count(name) as task_count"], - ) - count = count_data[0].task_count if count_data else 0 - data.append({"label": day_label, "value": count}) - - elif frequency == "weekly": - # ? LOGIC FOR WEEKLY FREQUENCY - start_date = getdate(f"{year}-{month}-01") - week = 1 - - # ? GET THE LAST DAY OF THE MONTH - last_day_of_month = add_months(start_date, 1) - timedelta(days=1) - - while start_date <= last_day_of_month: - week_end = min(add_days(start_date, 6), last_day_of_month) - count_data = frappe.db.get_list( - "QuickDo", - filters=[ - ["creation", "between", [start_date, week_end]], - ], - fields=["count(name) as task_count"], - ) - count = count_data[0].task_count if count_data else 0 - data.append({"label": f"Week {week}", "value": count}) - - week += 1 - start_date = add_days(week_end, 1) - - elif frequency == "monthly": - # ? LOGIC FOR MONTHLY FREQUENCY - for month_num in range(1, 13): - start_date = getdate(f"{year}-{month_num}-01") - end_date = add_months(start_date, 1) - timedelta(days=1) - count_data = frappe.db.get_list( - "QuickDo", - filters=[ - ["creation", "between", [start_date, end_date]], - ], - fields=["count(name) as task_count"], - ) - count = count_data[0].task_count if count_data else 0 - data.append({"label": months[month_num], "value": count}) - - # ? IF DATA IS EMPTY, LOG FOR DEBUGGING - if not data: - frappe.log_error( - "task_completions_trend_over_time", - "No data found for the frequency: {frequency} in {month}/{year}", - ) - - except Exception as e: - # ? LOG ERROR IF AN EXCEPTION OCCURS - frappe.log_error("Error in task_completions_trend", str(e)) - return { - "success": False, - "message": str(e), - "data": None, - } - - else: - # ? RETURN RESPONSE WITH SUCCESS MESSAGE AND DATA - return { - "success": True, - "data": data, - } + # DEFINE A DICTIONARY FOR MONTHS + months = { + 1: "January", + 2: "February", + 3: "March", + 4: "April", + 5: "May", + 6: "June", + 7: "July", + 8: "August", + 9: "September", + 10: "October", + 11: "November", + 12: "December", + } + try: + # ? GET TODAY'S DATE AND SET DEFAULT MONTH AND YEAR + today = getdate(datetime.today()) + month = month or today.month + year = year or today.year + + # ? INITIALIZE VARIABLES FOR CHART DATA + data = [] + + # ? FREQUENCY BASED CONDITIONS: DAILY, WEEKLY, OR MONTHLY + # ? LOGIC FOR DAILY FREQUENCY + if frequency == "daily": + # CALCULATE DAYS IN THE MONTH + start_date = getdate(f"{year}-{month}-01") + days_in_month = (add_months(start_date, 1) - start_date).days + + for day in range(1, days_in_month + 1): + day_label = f"{str(day).zfill(2)}-{str(month).zfill(2)}-{year}" + count_data = frappe.db.get_list( + "QuickDo", + filters=[ + ["creation", "=", getdate(f"{year}-{month}-{day}")], + ], + fields=["count(name) as task_count"], + ) + count = count_data[0].task_count if count_data else 0 + data.append({"label": day_label, "value": count}) + + elif frequency == "weekly": + # ? LOGIC FOR WEEKLY FREQUENCY + start_date = getdate(f"{year}-{month}-01") + week = 1 + + # ? GET THE LAST DAY OF THE MONTH + last_day_of_month = add_months(start_date, 1) - timedelta(days=1) + + while start_date <= last_day_of_month: + week_end = min(add_days(start_date, 6), last_day_of_month) + count_data = frappe.db.get_list( + "QuickDo", + filters=[ + ["creation", "between", [start_date, week_end]], + ], + fields=["count(name) as task_count"], + ) + count = count_data[0].task_count if count_data else 0 + data.append({"label": f"Week {week}", "value": count}) + + week += 1 + start_date = add_days(week_end, 1) + + elif frequency == "monthly": + # ? LOGIC FOR MONTHLY FREQUENCY + for month_num in range(1, 13): + start_date = getdate(f"{year}-{month_num}-01") + end_date = add_months(start_date, 1) - timedelta(days=1) + count_data = frappe.db.get_list( + "QuickDo", + filters=[ + ["creation", "between", [start_date, end_date]], + ], + fields=["count(name) as task_count"], + ) + count = count_data[0].task_count if count_data else 0 + data.append({"label": months[month_num], "value": count}) + + # ? IF DATA IS EMPTY, LOG FOR DEBUGGING + if not data: + frappe.log_error( + "task_completions_trend_over_time", + "No data found for the frequency: {frequency} in {month}/{year}", + ) + + except Exception as e: + # ? LOG ERROR IF AN EXCEPTION OCCURS + frappe.log_error("Error in task_completions_trend", str(e)) + return { + "success": False, + "message": str(e), + "data": None, + } + + else: + # ? RETURN RESPONSE WITH SUCCESS MESSAGE AND DATA + return { + "success": True, + "data": data, + } # ! api/method/quickdo.api.quickdo_due_date_trend_over_time # ? QUICKDO DUE DATE TREND OVER TIME @frappe.whitelist() def quickdo_due_date_trend_over_time( - frequency="monthly", - month=None, - year=None, + frequency="monthly", + month=None, + year=None, ): - # DEFINE A DICTIONARY FOR MONTHS - months = { - 1: "January", - 2: "February", - 3: "March", - 4: "April", - 5: "May", - 6: "June", - 7: "July", - 8: "August", - 9: "September", - 10: "October", - 11: "November", - 12: "December", - } - try: - # ? GET TODAY'S DATE AND SET DEFAULT MONTH AND YEAR - today = getdate(datetime.today()) - month = month or today.month - year = year or today.year - - # ? INITIALIZE VARIABLES FOR CHART DATA - data = [] - - # ? FREQUENCY BASED CONDITIONS: DAILY, WEEKLY, OR MONTHLY - # ? LOGIC FOR DAILY FREQUENCY - if frequency == "daily": - # CALCULATE DAYS IN THE MONTH - start_date = getdate(f"{year}-{month}-01") - days_in_month = (add_months(start_date, 1) - start_date).days - - for day in range(1, days_in_month + 1): - day_label = f"{str(day).zfill(2)}-{str(month).zfill(2)}-{year}" - count_data = frappe.db.get_list( - "QuickDo", - filters=[ - ["date", "=", getdate(f"{year}-{month}-{day}")], - ], - fields=["count(name) as task_count"], - ) - count = count_data[0].task_count if count_data else 0 - data.append({"label": day_label, "value": count}) - - elif frequency == "weekly": - # ? LOGIC FOR WEEKLY FREQUENCY - start_date = getdate(f"{year}-{month}-01") - week = 1 - - # ? GET THE LAST DAY OF THE MONTH - last_day_of_month = add_months(start_date, 1) - timedelta(days=1) - - while start_date <= last_day_of_month: - week_end = min(add_days(start_date, 6), last_day_of_month) - count_data = frappe.db.get_list( - "QuickDo", - filters=[ - ["date", "between", [start_date, week_end]], - ], - fields=["count(name) as task_count"], - ) - count = count_data[0].task_count if count_data else 0 - data.append({"label": f"Week {week}", "value": count}) - - week += 1 - start_date = add_days(week_end, 1) - - elif frequency == "monthly": - # ? LOGIC FOR MONTHLY FREQUENCY - for month_num in range(1, 13): - start_date = getdate(f"{year}-{month_num}-01") - end_date = add_months(start_date, 1) - timedelta(days=1) - count_data = frappe.db.get_list( - "QuickDo", - filters=[ - ["date", "between", [start_date, end_date]], - ], - fields=["count(name) as task_count"], - ) - count = count_data[0].task_count if count_data else 0 - data.append({"label": months[month_num], "value": count}) - - # ? IF DATA IS EMPTY, LOG FOR DEBUGGING - if not data: - frappe.log_error( - "task_completions_trend_over_time", - "No data found for the frequency: {frequency} in {month}/{year}", - ) - - except Exception as e: - # ? LOG ERROR IF AN EXCEPTION OCCURS - frappe.log_error("Error in task_completions_trend", str(e)) - return { - "success": False, - "message": str(e), - "data": None, - } - - else: - # ? RETURN RESPONSE WITH SUCCESS MESSAGE AND DATA - return { - "success": True, - "data": data, - } + # DEFINE A DICTIONARY FOR MONTHS + months = { + 1: "January", + 2: "February", + 3: "March", + 4: "April", + 5: "May", + 6: "June", + 7: "July", + 8: "August", + 9: "September", + 10: "October", + 11: "November", + 12: "December", + } + try: + # ? GET TODAY'S DATE AND SET DEFAULT MONTH AND YEAR + today = getdate(datetime.today()) + month = month or today.month + year = year or today.year + + # ? INITIALIZE VARIABLES FOR CHART DATA + data = [] + + # ? FREQUENCY BASED CONDITIONS: DAILY, WEEKLY, OR MONTHLY + # ? LOGIC FOR DAILY FREQUENCY + if frequency == "daily": + # CALCULATE DAYS IN THE MONTH + start_date = getdate(f"{year}-{month}-01") + days_in_month = (add_months(start_date, 1) - start_date).days + + for day in range(1, days_in_month + 1): + day_label = f"{str(day).zfill(2)}-{str(month).zfill(2)}-{year}" + count_data = frappe.db.get_list( + "QuickDo", + filters=[ + ["date", "=", getdate(f"{year}-{month}-{day}")], + ], + fields=["count(name) as task_count"], + ) + count = count_data[0].task_count if count_data else 0 + data.append({"label": day_label, "value": count}) + + elif frequency == "weekly": + # ? LOGIC FOR WEEKLY FREQUENCY + start_date = getdate(f"{year}-{month}-01") + week = 1 + + # ? GET THE LAST DAY OF THE MONTH + last_day_of_month = add_months(start_date, 1) - timedelta(days=1) + + while start_date <= last_day_of_month: + week_end = min(add_days(start_date, 6), last_day_of_month) + count_data = frappe.db.get_list( + "QuickDo", + filters=[ + ["date", "between", [start_date, week_end]], + ], + fields=["count(name) as task_count"], + ) + count = count_data[0].task_count if count_data else 0 + data.append({"label": f"Week {week}", "value": count}) + + week += 1 + start_date = add_days(week_end, 1) + + elif frequency == "monthly": + # ? LOGIC FOR MONTHLY FREQUENCY + for month_num in range(1, 13): + start_date = getdate(f"{year}-{month_num}-01") + end_date = add_months(start_date, 1) - timedelta(days=1) + count_data = frappe.db.get_list( + "QuickDo", + filters=[ + ["date", "between", [start_date, end_date]], + ], + fields=["count(name) as task_count"], + ) + count = count_data[0].task_count if count_data else 0 + data.append({"label": months[month_num], "value": count}) + + # ? IF DATA IS EMPTY, LOG FOR DEBUGGING + if not data: + frappe.log_error( + "task_completions_trend_over_time", + "No data found for the frequency: {frequency} in {month}/{year}", + ) + + except Exception as e: + # ? LOG ERROR IF AN EXCEPTION OCCURS + frappe.log_error("Error in task_completions_trend", str(e)) + return { + "success": False, + "message": str(e), + "data": None, + } + + else: + # ? RETURN RESPONSE WITH SUCCESS MESSAGE AND DATA + return { + "success": True, + "data": data, + } # ! api/method/quickdo.api.register_user # ? USER REGISTER API @frappe.whitelist(allow_guest=1) def register_user(full_name, email, password, redirect_to="/quickdo"): - try: - # ? CHECK IF THE USER IS ALREADY REGISTERED - user = frappe.db.get("User", {"email": email}) - - # ? IF THE USER IS REGISTERED - if user: - - # ? IF THE USE IS ENABLED - if user.enabled: - return { - "success": False, - "message": f"User is already registered with {email}", - } - - # ? IF THE USE IS DISABLED - else: - return { - "success": False, - "message": f"User is disabled with {email}", - } - - # ? IF THE USE DOES NOT EXISTS - else: - user = frappe.get_doc( - { - "doctype": "User", - "email": email, - "first_name": full_name, - "enabled": 1, - "new_password": password, - "user_type": "System User", - } - ) - user.flags.ignore_permissions = True - user.flags.ignore_password_policy = True - user.insert() - - # ! FOR NOW AS A QUICKDO USER - # ? SET DEFAULT ROLES TO THE USER - default_role = "QuickDo User" - if default_role: - user.add_roles(default_role) - - # ? SET THE DEFAULT REDIRECT PATH AFTER LOGIN - if redirect_to: - frappe.cache.hset("redirect_after_login", user.name, redirect_to) - - frappe.db.commit() - - # ? IF ERROR OCCURRED - except Exception as e: - frappe.db.rollback() - return { - "success": False, - "message": f"Something went wrong!: {str(e)}", - } - - # ? IF EVERYTHING GOES WELL - else: - return { - "success": True, - "message": f"User has been registered successfully {full_name}", - } + try: + # ? CHECK IF THE USER IS ALREADY REGISTERED + user = frappe.db.get("User", {"email": email}) + + # ? IF THE USER IS REGISTERED + if user: + # ? IF THE USE IS ENABLED + if user.enabled: + return { + "success": False, + "message": f"User is already registered with {email}", + } + + # ? IF THE USE IS DISABLED + else: + return { + "success": False, + "message": f"User is disabled with {email}", + } + + # ? IF THE USE DOES NOT EXISTS + else: + user = frappe.get_doc( + { + "doctype": "User", + "email": email, + "first_name": full_name, + "enabled": 1, + "new_password": password, + "user_type": "System User", + } + ) + user.flags.ignore_permissions = True + user.flags.ignore_password_policy = True + user.insert() + + # ! FOR NOW AS A QUICKDO USER + # ? SET DEFAULT ROLES TO THE USER + default_role = "QuickDo User" + if default_role: + user.add_roles(default_role) + + # ? SET THE DEFAULT REDIRECT PATH AFTER LOGIN + if redirect_to: + frappe.cache.hset("redirect_after_login", user.name, redirect_to) + + frappe.db.commit() + + # ? IF ERROR OCCURRED + except Exception as e: + frappe.db.rollback() + return { + "success": False, + "message": f"Something went wrong!: {str(e)}", + } + + # ? IF EVERYTHING GOES WELL + else: + return { + "success": True, + "message": f"User has been registered successfully {full_name}", + } diff --git a/quickdo/hooks.py b/quickdo/hooks.py index 725c08a..c5ffe35 100644 --- a/quickdo/hooks.py +++ b/quickdo/hooks.py @@ -18,13 +18,13 @@ # ? ADD QUICKDO APP TO FRAPPE APPS PAGE # Each item in the list will be shown as an app in the apps page add_to_apps_screen = [ - { - "name": "quickdo", - "logo": "/assets/quickdo/favicon.png", - "title": "QuickDo", - "route": "/app/quickdos", - # "has_permission": "quickdo.api.permission.has_app_permission", - } + { + "name": "quickdo", + "logo": "/assets/quickdo/favicon.png", + "title": "QuickDo", + "route": "/app/quickdos", + # "has_permission": "quickdo.api.permission.has_app_permission", + } ] # Includes in @@ -251,7 +251,7 @@ # ? SET WEBSITE DYNAMIC ROUTES FOR QUICKDO DASHBOARD website_route_rules = [ - {"from_route": "/quickdo/", "to_route": "quickdo"}, + {"from_route": "/quickdo/", "to_route": "quickdo"}, ] # ? FIXTURES diff --git a/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js b/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js index 7b40830..248e889 100644 --- a/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js +++ b/quickdo/quickdo/doctype/quickdo/quickdo_calendar.js @@ -1,15 +1,15 @@ frappe.views.calendar["QuickDo"] = { - field_map: { - start: "date", - end: "date", - id: "id", - title: "description", - allDay: 1, - progress: 100, - color: "color", - }, - gantt: true, - color: "color", - filters: [], - get_events_method: "frappe.desk.calendar.get_events", + field_map: { + start: "date", + end: "date", + id: "id", + title: "description", + allDay: 1, + progress: 100, + color: "color", + }, + gantt: true, + color: "color", + filters: [], + get_events_method: "frappe.desk.calendar.get_events", }; From 80336f55a883260681d7186d825e26e0b28cbc35 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 01:04:29 +0530 Subject: [PATCH 05/13] [Fix]: ESlint 3 --- frontend/tsconfig.app.json | 17 +++++++++-------- frontend/tsconfig.node.json | 14 +++++++------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index ff7ba78..7350a11 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -2,24 +2,23 @@ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], "module": "ESNext", "skipLibCheck": true, - - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", - - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "baseUrl": ".", "paths": { "@/*": [ @@ -27,5 +26,7 @@ ] } }, - "include": ["src"] -} + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 0d3d714..728a60a 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -1,22 +1,22 @@ { "compilerOptions": { "target": "ES2022", - "lib": ["ES2023"], + "lib": [ + "ES2023" + ], "module": "ESNext", "skipLibCheck": true, - - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, - - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["vite.config.ts"] -} + "include": [ + "vite.config.ts" + ] +} \ No newline at end of file From d3c7e84d15cb2c8f8479b440ebccd6a69d65efba Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 01:09:30 +0530 Subject: [PATCH 06/13] [Fix]: ESlint 4 --- quickdo/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickdo/api.py b/quickdo/api.py index 1148d92..5beed32 100644 --- a/quickdo/api.py +++ b/quickdo/api.py @@ -676,7 +676,7 @@ def register_user(full_name, email, password, redirect_to="/quickdo"): # ! FOR NOW AS A QUICKDO USER # ? SET DEFAULT ROLES TO THE USER default_role = "QuickDo User" - if default_role: + if isinstance(default_role, str) and default_role.strip(): user.add_roles(default_role) # ? SET THE DEFAULT REDIRECT PATH AFTER LOGIN From c64c93f199a2fbfb57f6a563716879a4067ff943 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 01:23:44 +0530 Subject: [PATCH 07/13] [Chore]: minor fixes related to Kanban and auto set allocated field --- quickdo/fixtures/kanban_board.json | 18 +++++++++--------- quickdo/quickdo/doctype/quickdo/quickdo.json | 9 +++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/quickdo/fixtures/kanban_board.json b/quickdo/fixtures/kanban_board.json index 2640646..650dcee 100644 --- a/quickdo/fixtures/kanban_board.json +++ b/quickdo/fixtures/kanban_board.json @@ -3,30 +3,30 @@ "columns": [ { "column_name": "Open", - "indicator": "Gray", - "order": "[\"ToDo-08-09-24-00002\", \"ToDo-08-09-24-00001\"]", + "indicator": "Blue", + "order": "[\"ToDo-18-07-25-00004\", \"ToDo-18-07-25-00003\"]", "status": "Active" }, { - "column_name": "Closed", - "indicator": "Gray", - "order": "[\"ToDo-08-09-24-00003\"]", + "column_name": "Completed", + "indicator": "Green", + "order": "[\"ToDo-18-07-25-00001\"]", "status": "Active" }, { "column_name": "Cancelled", - "indicator": "Gray", - "order": "[\"ToDo-08-09-24-00004\"]", + "indicator": "Red", + "order": "[\"ToDo-18-07-25-00002\"]", "status": "Active" } ], "docstatus": 0, "doctype": "Kanban Board", "field_name": "status", - "fields": "[\"date\", \"is_important\", \"send_reminder\", \"reference_type\"]", + "fields": "[\"allocated_to\",\"date\",\"is_important\",\"send_reminder\",\"creation\",\"modified\"]", "filters": null, "kanban_board_name": "QuickDo Kanban", - "modified": "2024-09-08 20:56:43.245103", + "modified": "2025-07-18 01:21:21.477332", "name": "QuickDo Kanban", "private": 0, "reference_doctype": "QuickDo", diff --git a/quickdo/quickdo/doctype/quickdo/quickdo.json b/quickdo/quickdo/doctype/quickdo/quickdo.json index af3a5ee..e6b45de 100644 --- a/quickdo/quickdo/doctype/quickdo/quickdo.json +++ b/quickdo/quickdo/doctype/quickdo/quickdo.json @@ -75,10 +75,11 @@ "oldfieldtype": "Date" }, { + "default": "__user", "fieldname": "allocated_to", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_global_search": 1, + "in_list_view": 1, "label": "Allocated To", "options": "User" }, @@ -108,7 +109,6 @@ { "fieldname": "reference_type", "fieldtype": "Link", - "in_list_view": 1, "label": "Reference Type", "oldfieldname": "reference_type", "oldfieldtype": "Data", @@ -202,7 +202,7 @@ "is_calendar_and_gantt": 1, "links": [], "make_attachments_public": 1, - "modified": "2024-10-07 10:43:19.590481", + "modified": "2025-07-18 01:20:50.068305", "modified_by": "Administrator", "module": "QuickDo", "name": "QuickDo", @@ -223,6 +223,7 @@ } ], "quick_entry": 1, + "row_format": "Dynamic", "search_fields": "description", "show_title_field_in_link": 1, "sort_field": "creation", @@ -232,4 +233,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 1 -} \ No newline at end of file +} From 4a47f0ad7fc9b1f6d15b457f1b41d9a12ddad606 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Fri, 18 Jul 2025 01:39:30 +0530 Subject: [PATCH 08/13] [Chore]: Fixed the header border --- frontend/src/components/layout/navbar.tsx | 2 +- frontend/src/components/layout/sidebar.tsx | 2 +- frontend/src/pages/category/category-view.tsx | 2 +- frontend/src/pages/quickdo/calendar-view.tsx | 2 +- frontend/src/pages/quickdo/dashboard-view.tsx | 2 +- frontend/src/pages/quickdo/group-by-view.tsx | 2 +- frontend/src/pages/quickdo/inbox-view.tsx | 2 +- frontend/src/pages/quickdo/kanban-view.tsx | 2 +- frontend/src/pages/quickdo/my-day-view.tsx | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/layout/navbar.tsx b/frontend/src/components/layout/navbar.tsx index 41ea70f..f75d604 100644 --- a/frontend/src/components/layout/navbar.tsx +++ b/frontend/src/components/layout/navbar.tsx @@ -36,7 +36,7 @@ const Navbar = () => { return ( <> {/* NAVBAR */} -
+