jey_theme/CLAUDE.md

5.5 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this app is

Frappe/ERPNext v15 app that does three things:

  1. Overrides the desk UI with a multi-theme visual system (CSS + JS bundle).
  2. Hides the Quality Management and Subcontracting modules app-wide (URL/API/bootinfo/permissions).
  3. Maintains a custom Taxes Az workspace on every migrate.

Installed into frappe-bench as a normal app. Python code is at jey_theme/ (Frappe app package). Frontend assets live at jey_theme/public/{css,js}.

Commands

Run from frappe-bench/ (the bench root), not this app dir.

  • bench build --app jey_theme — rebuild JS bundle after editing jey_theme/public/js/jey_theme.js.
  • CSS-only changes: no build needed, just hard-reload the browser.
  • bench migrate — runs the after_migrate hook that rebuilds the Taxes Az workspace from setup/taxes_workspace.py.
  • bench execute jey_theme.setup.taxes_workspace.setup — re-run the workspace setup without a full migrate.
  • bench restart — reload Python workers after editing access_control.py or hooks.py.
  • No test suite, no linter config in this repo.

Architecture

Python: access control (jey_theme/access_control.py + hooks.py)

Goal: make blocked modules invisible and unreachable for every user, including Administrator.

  • BLOCKED_MODULES / BLOCKED_DOCTYPES_EXTRA — edit these frozensets to change what's blocked.
  • Three defense layers wired in hooks.py:
    • before_request — blocks /api/resource/<Doctype>, /app/<slug>, and form_dict-based calls at the HTTP boundary (catches Administrator, who bypasses has_permission).
    • has_permission = {"*": ...} — per-doc deny for non-admin users who have role access.
    • extend_bootinfo — strips blocked items from allowed_modules, modules, user.can_*, workspaces.pages, workspace_sidebar_item, module_wise_workspaces, app_data, desktop_icons, etc., so the client router/sidebar never sees them.
  • Slug/doctype/workspace sets are cached on frappe.local per-request.
  • When adding a new block vector: extend extend_bootinfo rather than relying only on has_permission, because the client has many independent boot-derived UIs.

Python: Taxes Az workspace (jey_theme/setup/taxes_workspace.py)

Idempotent setup run via after_migrate.

  • Builds a Workspace named Taxes Az with three cards (Declarations, Setup, Tools), a Workspace Sidebar listing every taxes_az doctype, and a Desktop Icon that routes via the sidebar.
  • The doctype list is a static snapshot in DECLARATIONS_DOCTYPES / SETUP_DOCTYPES / TOOLS_DOCTYPES. Adding a DocType to the taxes_az app does NOT auto-include it — append to the tuple and re-migrate.
  • _cleanup_obsolete() removes legacy docs owned by this app (currently "Taxes"). Only touches records where app == "jey_theme" — never deletes ERPNext/other-app docs.

Frontend: multi-theme system

Bundle entry: jey_theme/public/js/jey_theme.js. CSS: jey_theme/public/css/{shared,theme_chrome,theme_modern}.css. All four are loaded via app_include_css / app_include_js in hooks.py for both desk and web contexts.

  • Theme selector lives in data-jey-theme on <html>, stored in localStorage("jey-theme"). Default: chrome. Set before first paint by an IIFE at the top of jey_theme.js.
  • Themes supported: chrome (metallic rings), modern (frosted glass). No dark/light variants — each theme is self-contained.
  • Theme-specific CSS MUST be wrapped in [data-jey-theme="themename"]. Keyframes MUST be prefixed jey-<theme>-* to avoid cross-theme name collisions.
  • Shared CSS (checkboxes, icon base sizing, switcher UI, column-resize rules) lives in shared.css without theme selectors.

Frontend: per-theme icons

  • THEME_ICONS[themeName] in jey_theme.js maps icon keys to inline SVG path strings. Missing keys fall back to THEME_ICONS.chrome, then _fallback.
  • JS replaces <img class="app-icon"> / .folder-icon with inline <svg class="jey-icon"> per current theme.
  • window.jeyRebuildIcons() tears down and rebuilds all icon DOM — called on theme switch.
  • SVGs may carry a cls (e.g. ico-buying) for shared CSS animations.

Frontend: early-paint tricks in jey_theme.js

The top IIFE runs before paint to avoid FOUC:

  • Sets data-jey-theme immediately.
  • Defaults container_fullwidth = "true" on fresh install.
  • Reads all jey-col-widths:<Doctype>:<field> localStorage keys and injects a <style id="jey-col-resize-styles"> with per-grid column widths, plus a MutationObserver that stamps data-jey-grid on grids as they appear.

Keep this IIFE minimal and synchronous — anything slow here delays first paint.

Adding a new theme

  1. Create jey_theme/public/css/theme_mytheme.css with [data-jey-theme="mytheme"] selectors. Prefix keyframes jey-mytheme-*.
  2. Add THEME_ICONS.mytheme = { ... } in jey_theme.js (only override keys you care about).
  3. Add an entry to JEY_THEMES array in the switcher section of jey_theme.js.
  4. Add the CSS file to both app_include_css and web_include_css in hooks.py.
  5. bench build --app jey_theme.

Rules

  • Themes can change anything (layout, positioning, colors, fonts, visibility, animations, DOM structure) — not just icons. Use JS (jeyRebuildIcons / theme-switch hooks) for DOM restructuring.
  • Never add [data-theme="dark"] or light/dark variant rules — themes are self-contained (no inherited dark/light axis).
  • Don't touch ERPNext-owned records in setup scripts; gate deletes on app == "jey_theme".