From c5db7528d8ea91483f2f4c4964f9911c0d7d6b5e Mon Sep 17 00:00:00 2001 From: Rex Lorenzo Date: Thu, 18 Jun 2026 09:34:37 -0700 Subject: [PATCH] docs: add agent guidance files and update onboarding README - Add CLAUDE.md, AGENTS.md, DESIGN.md, and PRODUCT.md as the source of truth for agent, design-system, and product conventions - README: correct the pre-commit hook description, document the Jenkins pre-push hook and Claude Code tooling, add CI status badges, and bump the IDE prerequisite to Visual Studio 2026 - Ignore .claude/ (per-developer agent config) --- .gitignore | 3 + AGENTS.md | 1 + CLAUDE.md | 63 +++++++++++++ DESIGN.md | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++ PRODUCT.md | 53 +++++++++++ README.md | 79 ++++++++++++++-- 6 files changed, 464 insertions(+), 8 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 DESIGN.md create mode 100644 PRODUCT.md diff --git a/.gitignore b/.gitignore index 6b4cf6e2d..30c8786b2 100644 --- a/.gitignore +++ b/.gitignore @@ -504,3 +504,6 @@ web/Areas/Effort/Scripts/Effort_Database_Schema_And_Data_LEGACY.txt VueApp/.fallow/ jscpd-report/ inspect-report/ + +# Agent folders +.claude diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..b75d55742 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +See CLAUDE.md for all project instructions. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..c7105394e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,63 @@ +# CLAUDE.md + +When I ask a question or make an observation, respond with an answer - do NOT jump to making code changes unless I explicitly ask for them. + +## Environment & Commands + +**Windows-only:** Use `dir`, `type`, backslashes. **Line endings must be CRLF (`\r\n`)** - never use bare LF. + +- Run via npm scripts, never direct .NET/dotnet commands (avoids lock-file conflicts): Dev `npm run dev` | Test `npm run test` (`test:backend`, `test:frontend`) | Lint `npm run lint ` | Build `npm run verify:build` +- **Stale cache**: Add `-- --clear-cache` to `verify:build` or `lint` if builds fail with cached errors + +## Architecture + +- **Backend**: ASP.NET 10, `web/Areas/{AreaName}/` | **Frontend**: Vue 3 multi-SPA, Vite → `wwwroot/vue/` +- **DB**: SQL Server 2016 + EF Core (CTS, RAPS, AAUD schemas) | **Auth**: CAS, `[Permission(Allow = "SVMSecure.{Area}.{Action}")]` +- **Identity:** `AaudUser.AaudUserId` = `Person.PersonId`. If mismatched, TEST DB needs refresh. +- **Design system (UI)**: Colors, typography, components (buttons & button colors, dialogs, badges, banners, inputs, selects, nav), the `
` landmark rule, WCAG-AA contrast rules, and Do's/Don'ts all live in [DESIGN.md](DESIGN.md) — read it before building or changing UI. Always use Quasar components. +- **VueUse**: Prefer VueUse composables over hand-rolled reactive logic. +- **O(n²) lookups**: Never nest `.find()`/`.filter()`/`.some()` inside `.map()`/`.forEach()`/`for` when both arrays can grow. Pre-build a `Map`/`Set` once, then `.get()`/`.has()` in the loop. Small fixed reference lists (terms, roles, ~10 items) are fine. +- **Plurals**: Use `inflect("word", count)` from the `inflection` package — never hand-roll ternaries for noun pluralization + +## Database & EF Core + +- **SQL Server 2016** — no `STRING_AGG`, `TRIM`, `CONCAT_WS`, `GREATEST/LEAST` +- Prefer EF entities over raw SQL. Raw SQL only for non-EF tables via `GetConnectionString()`. Never mix raw SQL + EF entities (causes auth failures). +- **Read-only queries**: Always `.AsNoTracking()` | `.Include()` before `.Select()` is unnecessary — EF resolves navigations in projections +- **Correlated subqueries**: Avoid `.Any()` on large tables inside `.Where()`/`.CountAsync()` — pre-load ID sets then use `.Contains()`, or replace with `.Join()` +- **`.Contains()` with large collections (10+)**: Wrap with `EF.Parameter()` for `OPENJSON` translation: `.Where(x => EF.Parameter(largeList).Contains(x.Id))`. Small collections (<10) are fine without it. +- **Thread safety**: DbContext not thread-safe — no parallel EF queries + +## API & Cross-Environment + +- **Routes**: Absolute `/api/{area}/{controller}` + `ApiController` base. Never `[Area]` on APIs (causes 403) +- **Frontend API calls**: Service layer + `useFetch()`, never raw `fetch()` (must unwrap `{ result, success }`) +- **API URL**: `${import.meta.env.VITE_API_URL}` — never hardcode `/api/` (TEST uses `/2/` prefix) +- **Subpath PathBase (`/2`)**: TEST/PROD run VIPER 2 under a `/2` PathBase (IIS sub-app), legacy VIPER 1 at `/`; with no base locally, these bugs surface only on TEST/PROD (not in unit tests). Use `~/` for app-root redirects, never bare `/` (escapes to the legacy site). Guards matching root-relative paths (`/api`, `/welcome`) must strip the base off the base-prefixed `ReturnUrl` (`/2/...`) or use `Request.PathBase`. `RedirectToAction`, `@Url.Content("~/")`, and tag-helpers include the base; raw string paths (`Redirect("/x")`, `returnUrl.StartsWith("/api")`) don't. +- **Auth**: `[Permission(Allow = "SVMSecure.{Area}")]` — authenticate before validating params + +## C# Standards + +- **Exceptions**: Catch specific types (`DbUpdateException`, `SqlException`, `InvalidOperationException`). Never generic `catch (Exception ex)`. +- **Paths**: `Path.Join()` not `Path.Combine()` | **DateTime**: prefer `DateTimeKind.Local` +- **LINQ**: `.Where()` for filtering (not `if` inside foreach), `.Select()` for mapping (not foreach + Add) +- **O(n²) lookups**: Don't nest `.FirstOrDefault()`/`.Single()`/`.Any()` inside a `foreach`/`.Select()` over another list. Materialize once with `.ToDictionary()` or `.ToLookup()`, then `TryGetValue` in the loop. For EF, this is N+1 — see Database & EF Core "Correlated subqueries". +- **Mapperly**: Prefer over manual property mapping. Static partial mapper class per area with `[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.None)]`. Use `[MapperIgnoreTarget]` for computed properties, manual wrappers for transforms. Align entity/DTO names — use EF `HasColumnName()` to decouple from DB columns. +- **Scrutor**: Convention-based DI auto-registers `*Service`/`*Validator` from configured namespaces — prefer over manual `AddScoped`. Follow `IFooService`/`FooService` naming. Explicit `AddScoped` before Scrutor takes precedence (`RegistrationStrategy.Skip`). +- **Comments**: Sparingly, why-not-what, complex logic only. +- **Bug fixes**: Verify ALL code paths using affected logic. Check for duplicate/parallel implementations and fix consistently or DRY into shared method. + +## Security + +- **Log Injection**: Sanitize user input before logging via `LogSanitizer` (`SanitizeId()`, `SanitizeString()`, `SanitizeYear()`). Skip hard-coded strings, enums, DB values. + +## Testing & Git + +- **UI**: Test UI changes with Playwright MCP (modals, forms, keyboard nav) +- **API**: Use Playwright MCP to visit endpoints — APIs require browser auth, `curl` fails +- **Git**: NEVER stage files until after code review. Workflow: changes → test → lint → summary → approval → stage +- **Branch & merge flow**: Branch off `main`, named `feature/`|`fix/`|etc. plus the JIRA ticket if applicable (e.g. `feature/VPR-104-clinical-scheduler`). After code review, merge into `Development` and push, which deploys to TEST. After the PR is approved on TEST, merge into `main`. Every change goes through `Development` first. +- **Never branch off `Development`**: it is a merge/deploy target, never a base. A branch being "behind `Development`" is expected and not a concern (you never sync or rebase from it). Its history is messy by design and never rewritten. +- **Squash during review**: If a branch is still unmerged and worked by a single developer, squash code-review fixes into the relevant existing commit for cleaner history rather than stacking "address review" commits. +- **Plan/smoketest notes**: `PLAN-*.md` and `SMOKETEST-*.md` files at the repo root are local working notes — never stage or commit them. They are intentionally left untracked. +- **Commit messages**: Conventional Commits — `type(scope): subject` (types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`; prefer `feat` for new behavior). Prefix ticket ID from branch name if present (e.g. `VPR-104 fix(a11y): ...`). Subject: imperative, max 72 chars, no trailing period, captures intent/big picture not implementation, optimized for future readers. Body bullets only when subject is insufficient — each bullet must answer "what would be unclear or risky if omitted?"; merge related items and skip internal plumbing, helpers, and test scaffolding unless they are the primary change. Wrap body at 72 chars. Omit body for single-focus changes. diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 000000000..76dd1c64f --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,273 @@ +--- +name: VIPER +description: Internal web application suite for the UC Davis Weill School of Veterinary Medicine. +colors: + aggie-blue: "#022851" + blue-hover: "#033266" + blue-secondary: "#4b6983" + aggie-gold: "#ffbf00" + gold-accent: "#ffc519" + gold-nav: "#ffd24c" + redwood: "#266041" + merlot: "#79242f" + tahoe: "#00b2e3" + poppy: "#f18a00" + arboretum: "#00c4b3" + cabernet: "#481268" + ink: "#1d1d1d" + body-grey: "#666666" + surface: "#ffffff" + table-header: "#eeeeee" + blue-10: "#cdd6e0" +typography: + display: + fontFamily: "Proxima Nova, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Arial, sans-serif" + fontSize: "clamp(2.75rem, 5.5vw, 4.25rem)" + fontWeight: 800 + lineHeight: 1 + letterSpacing: "-0.02em" + chrome: + fontFamily: "Proxima Nova, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Arial, sans-serif" + fontWeight: 500 + letterSpacing: "normal" + heading: + fontFamily: "Roboto, -apple-system, Helvetica Neue, Helvetica, Arial, sans-serif" + fontSize: "1.2rem" + fontWeight: 700 + lineHeight: 1.2 + body: + fontFamily: "Roboto, -apple-system, Helvetica Neue, Helvetica, Arial, sans-serif" + fontSize: "1rem" + fontWeight: 400 + lineHeight: 1.5 + label: + fontFamily: "Roboto, -apple-system, Helvetica Neue, Helvetica, Arial, sans-serif" + fontSize: "0.75rem" + fontWeight: 500 + letterSpacing: "0.04em" +rounded: + none: "0" + sm: "0.25rem" + default: "4px" +spacing: + xs: "4px" + sm: "8px" + md: "16px" + lg: "24px" + xl: "48px" +components: + button-primary: + backgroundColor: "{colors.aggie-blue}" + textColor: "{colors.surface}" + rounded: "{rounded.default}" + button-positive: + backgroundColor: "{colors.redwood}" + textColor: "{colors.surface}" + rounded: "{rounded.default}" + button-negative: + backgroundColor: "{colors.merlot}" + textColor: "{colors.surface}" + rounded: "{rounded.default}" + button-info: + backgroundColor: "{colors.tahoe}" + textColor: "{colors.ink}" + rounded: "{rounded.default}" + button-warning: + backgroundColor: "{colors.gold-accent}" + textColor: "{colors.ink}" + rounded: "{rounded.default}" + button-secondary: + backgroundColor: "{colors.blue-secondary}" + textColor: "{colors.surface}" + rounded: "{rounded.default}" + card: + backgroundColor: "{colors.surface}" + textColor: "{colors.ink}" + rounded: "{rounded.default}" + padding: "16px" + welcome-cta: + backgroundColor: "{colors.aggie-blue}" + textColor: "{colors.surface}" + rounded: "{rounded.none}" + padding: "16px 20px" + welcome-cta-hover: + backgroundColor: "{colors.blue-hover}" + textColor: "{colors.surface}" +--- + +# Design System: VIPER + +## 1. Overview + +**Creative North Star: "The Teaching Hospital"** + +VIPER wears two faces, and the system is the building that holds both. The public face is the lobby: a full-bleed photographic welcome where the work of the school (an eye exam, a foal, the SVM building under California sky) is the hero, framed in Aggie Blue with a thin gold rule, calm and institutional. The working interior is the clinic floor: dense, fast, legible, every pixel earning its place because the people here are mid-task and the screen is a tool, not a destination. The brand greets you at the door; the product gets out of your way once you are inside. + +This is institutional software for a public university veterinary school, not a consumer SaaS product, and it refuses the tells of one. No hero-metric template, no gradient text, no glassmorphism, no identical icon-card grids, no marketing-deck buzzwords. The color discipline is the UC Davis brand discipline: Aggie Blue carries the surface, gold is a rare accent that never dominates, and everything is held to WCAG AA because the audience includes clinicians, staff, faculty, and students using this daily under real time pressure. Built on Quasar, the visual vocabulary is deliberately restrained: flat surfaces, compressed headings, tight rem-based spacing, and components reused rather than reinvented. + +The system is bilingual by design. Brand chrome (the blue header bar, the unauthenticated welcome splash) speaks in **Proxima Nova**, the UC Davis campus typeface. The application workspace speaks in **Roboto**, narrower and quieter so dense tables and forms stay readable. The two never mix on the same surface. + +**Key Characteristics:** +- Aggie Blue surface, gold as a disciplined accent (never dominant) +- Two typefaces with one job each: Proxima Nova for brand chrome, Roboto for the workspace +- Flat by default; elevation is reserved, not decorative +- WCAG AA is a floor, not a goal: greys, gold, and tab states are all contrast-corrected +- Dense, compressed, functional product UI; warm, photographic brand surfaces +- rem everywhere, no inline styles, no `!important` outside documented contrast overrides + +## 2. Colors: The Aggie Palette + +A UC Davis institutional palette: Aggie Blue is the foundation, Aggie Gold is the signature accent, and a small secondary set adds status and emphasis. All values are canonical UC Davis brand hex; the blue, gold, and black scales each run a full 10–100 ramp in `VueApp/src/styles/colors.css`. + +### Primary + +- **Aggie Blue** (#022851): The load-bearing brand color. The top header chrome, primary buttons, the welcome splash background, active nav text, link color, and skip-to-content chip. This is the surface that says "UC Davis." Quasar `primary`. The full blue ramp (`--ucdavis-blue-10` #cdd6e0 → `--ucdavis-blue-100` #022851) provides hover (`blue-90` #033266), active-row tints (`blue-10`), and tree/arrow strokes. +- **Aggie Gold** (#ffbf00, accent shade #ffc519): The signature accent. The 3px rule under the header bar, the gold brand-mark tile behind the rod-of-asclepius, the welcome card's top border, the CTA arrow, and focus rings on dark surfaces. Quasar `accent` and `warning` both map to `gold-90` (#ffc519). The gold section-nav bar uses lighter ramp steps (`gold-70` #ffd24c band, `gold-40` #ffe599 selected). + +### Secondary + +- **Blue 70** (#4b6983): Quasar `secondary`. Secondary buttons and muted blue chrome. (Note: the CSS ramp token `--ucdavis-blue-70` is `#355b85`; the Quasar semantic `secondary` is `#4b6983`. Treat the Quasar value as canonical for `color="secondary"`.) +- **Tahoe** (#00b2e3): Quasar `info`. Informational/tertiary actions. Light enough that it always pairs with `text-color="dark"`. + +### Tertiary + +- **Redwood** (#266041): Quasar `positive`. Success and create actions. +- **Merlot** (#79242f): Quasar `negative`. Danger and delete actions. +- **Poppy** (#f18a00): Tips and highlights only. A warm orange used sparingly for callouts. +- **Arboretum** (#00c4b3) / **Cabernet** (#481268): Secondary-palette accents, never dominant. Reserve for charts, tags, and categorical distinction. + +### Neutral + +- **Ink** (#1d1d1d): Quasar `dark`; default high-contrast text. `dark-page` is #121212. +- **Body Grey** (#666666): `--ucdavis-black-60`, the AA-safe muted text color. Quasar's default `.text-grey`/`.bg-grey` are remapped to this so muted text still clears 4.5:1. +- **Surface** (#ffffff): Card and panel background. +- **Table Header** (#eeeeee): Sticky `q-table` header fill. + +### Named Rules +**The Gold-Is-Accent Rule.** Gold is never a dominant surface and never body text. Per the UC Davis secondary-palette guidance, accents are "never dominant." Gold appears as a thin rule, a focus ring, a brand-mark tile, or a single arrow — measured in pixels of width, not percent of screen. Aggie Blue carries weight; gold punctuates it. + +**The Bright-Gold-Never-On-White Rule.** `#ffbf00`/`#ffc519` on white fails AA at text sizes. For gold *text* on light backgrounds, use the darkened `--text-warning` (#664d03). Bright gold is a background and accent color only. + +**The AA-Floor Rule.** Every foreground/background pair clears WCAG AA (4.5:1 body, 3:1 large/UI). Quasar greys (`grey`, `grey-5`, `grey-6`) are remapped to `grey-7`; inactive tabs are de-faded (`tabs-no-fade`); input clear-button and tree-arrow opacities are bumped. Contrast correction is a system-level commitment, not a per-screen afterthought. + +## 3. Typography + +**Display / Brand Font:** Proxima Nova (with Roboto, then system-sans fallback). Self-hosted woff2 at weights 400/500/700/800; weight 900 downshifts to 800 (the family caps there). +**Body / Workspace Font:** Roboto (variable woff2, weights 100–900, with system-sans fallback). +**Icon Font:** Material Icons (self-hosted woff2). +**Print-Only:** Ryman Eco is the UC Davis print display face; it is **not** loaded on the web. Arial / Aptos are the brand-approved fallbacks when Proxima is unavailable. + +**Character:** Proxima Nova is the warm, confident geometric campus voice; Roboto is the neutral, space-efficient workhorse for data-dense screens. The contrast axis between them is *role*, not style: brand vs. work. Pairing them keeps the institutional identity at the edges while letting the interior breathe. + +### Hierarchy + +- **Display** (Proxima Nova 800, `clamp(2.75rem, 5.5vw, 4.25rem)`, line-height 1, letter-spacing −0.02em): The welcome-splash headline only. The single place display-scale type appears. +- **Chrome / Brand Title** (Proxima Nova 500, ~1.6em): The blue top header bar and brand lockup text. +- **Heading** (Roboto 700, 1.2rem, line-height 1.2): In-app page and dialog headings (`h1`–`h3` inside `q-page-container`/`q-dialog` are deliberately compressed to 1.2rem). `h4` is 1.1rem, `h5` 1rem bold, `h6` 1rem regular. +- **Body** (Roboto 400, base 14px → 16px ≥768px): All application copy. Root font-size scales up at the 768px breakpoint, so rem-based sizing grows with it. Cap prose at 65–75ch. +- **Label** (Roboto 500, 0.75rem, letter-spacing 0.04em): Left-nav subheaders, footer text, breadcrumbs, skip-to-content. Short labels only. + +### Named Rules +**The Two-Font Rule.** Proxima Nova belongs to brand chrome (the blue header bar, the welcome splash). Roboto belongs to the workspace (everything inside `q-page-container` and the gold section nav, which is intentionally Roboto so its many items fit). Never set workspace body copy in Proxima; never set brand chrome in Roboto. + +**The Compressed-Heading Rule.** Inside the app, headings are 1–1.2rem and bold, not display-scale. Hierarchy in the workspace comes from weight and spacing, not size. Display-scale type (>2.75rem) is reserved for brand surfaces. An in-app `h1` that looks like a marketing hero is wrong. + +## 4. Elevation + +The system is flat by default. Surfaces sit on the page with borders and tonal fills rather than drop shadows; depth is the exception, earned by state or by the one dramatic brand moment. Per CLAUDE.md, "cards are the lazy answer" — containers are used only when they are the right affordance, and never nested. + +### Shadow Vocabulary + +- **Focus ring** (`box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb`): The keyboard-focus halo on form controls and buttons. On the dark welcome splash this becomes a gold ring (`0 0 0 0.1875rem rgba(255,197,25,0.85)`) since default outlines vanish on the photo. +- **Splash card** (`box-shadow: 0 1.875rem 5rem rgba(0,0,0,0.45)`): The single heavy elevation in the system — the white sign-in card lifting off the full-bleed hero photo. Dramatic on purpose, and used exactly once. +- **Sticky table header** (`background-color: #eee`): Tonal layering, not shadow — the sticky `q-table` header reads as elevated through fill and stickiness alone. + +### Named Rules +**The Flat-By-Default Rule.** Workspace surfaces are flat at rest. Shadow appears as a response to focus, or as the single deliberate lift of the welcome card over its hero photo. If a routine panel or card has a drop shadow "for depth," remove it. + +## 5. Components + +VIPER is built on Quasar; the prescription is **always use Quasar components**, styled with brand tokens, not bespoke markup. The pieces below are the ones that carry the system's character. + +### Buttons + +- **Shape:** Quasar default radius (~4px); the brand-splash CTA is square (0 radius) with a gold top accent. +- **Color roles (brand-mapped):** Primary action → `primary` (Aggie Blue); Success/Create → `positive` (Redwood); Danger/Delete → `negative` (Merlot); Info/Tertiary → `info text-color="dark"` (Tahoe); Warning/Caution → `warning text-color="dark"` (Gold); Secondary → `secondary` (Blue 70). +- **Loading state:** A `q-btn` with a text label plus `:loading` needs a `#loading` slot (`` + the label text). Icon-only buttons use the default spinner. +- **Interactive non-buttons:** Anything clickable that isn't a `q-btn` needs `@keyup.enter` + `@keyup.space` + `tabindex="0"` + `role="button"` + `aria-label`. + +### Badges + +- **Use `StatusBadge`** (wraps `q-badge`, auto-pairs `text-color` with `color` for contrast). Pass the label via the `label` prop or default slot. Avoid raw `q-badge` with manual `getAccessibleTextColor`. + +### Banners + +- **Use `StatusBanner`** in Vue SPAs (`type="success|error|warning|info"`). Only `type="error"` is assertive (`role="alert"`) by default; everything else is polite (`role="status"`). Override with the `live` prop only when needed: `live="assertive"` for a warning/info banner shown in direct response to a user action (post-submit validation, time-sensitive notice), `live="off"` for a purely decorative banner with no dynamic content (drops the role entirely). Don't reach for `type="warning"` to force an assertive announcement on a persistent state indicator — that's what `live="assertive"` is for, and most page-load warnings should stay polite. Razor pages use `q-banner` with accessible classes (`bg-warning text-dark`, `role="status"`/`"alert"`). Error surfaces outside `StatusBanner` use the `.error-surface` treatment (12% Merlot tint, Merlot left accent, Merlot text). + +### Cards / Containers + +- **Corner Style:** Quasar default (~4px); brand splash card is square with a 3px gold top border. +- **Background:** White surface on the workspace; the welcome card is white over the Aggie Blue hero. +- **Shadow Strategy:** Flat in the workspace (see Elevation). Never nest cards. +- **Internal Padding:** rem-based Quasar spacing (`md` 16px typical). + +### Inputs / Fields + +- **Style:** Quasar `q-field` / `q-input` / `q-select`. Selects are **always `dense` + `options-dense`**. +- **Focus:** The white+blue focus ring (see Elevation). Clear-button opacity is forced to 1 for contrast. + +### Dialogs + +- **Every `q-dialog`** needs (1) an accessible name via `aria-labelledby` → the title `id` (or `aria-label` if there's no visible title), and (2) a visible close affordance. Persistent SPA dialogs use `@click="handleClose"` instead of `v-close-popup`. Error banners go in a separate `q-card-section` below the header. **Footer actions are Submit + Delete only — no Cancel** (the X dismisses). Canonical header pattern: + + ```html + + + +
Dialog Title
+ + +
+ ``` + +### Navigation + +- **Top chrome:** Aggie Blue bar (`#mainLayoutHeader`, min-height 86px ≥768px) in Proxima Nova 500, carrying the `.viper-brand` lockup — a gold tile (`--ucdavis-gold-100`) holding the white rod-of-asclepius mark, beside the school-name lockup image (lockup name hidden below 1024px). +- **Section nav:** A gold band (`gold-70` #ffd24c) in Roboto 400; the selected item gets `gold-40` (#ffe599). +- **Left drawer:** `#leftNavMenu` with bold section headers, 500-weight subheaders, indent tiers; active router links tint to `blue-10` with `primary` text at weight 500. The mobile overlay-drawer glow is suppressed. + +### Welcome Splash (signature component) +The unauthenticated landing (`web/Views/Home/Welcome.cshtml`, `Layout = null`): full-bleed hero photo (one of five, randomized per load, served `image-set` AVIF→JPG) under an Aggie-Blue gradient, with an editorial column (gold rule + Proxima 800 headline + tagline) and a white sign-in card carrying a single CAS "Sign in →" CTA. The one place in the system that goes drenched, photographic, and display-scale. A standalone stylesheet (`welcome.css`) redeclares the brand tokens because it cannot load the Vue/Quasar token files. + +### Landmarks + +- **Exactly one `
` per page.** Razor pages get it from `_VIPERLayout.cshtml`; Vue SPAs get it from `ViewerLayout`/`ViperLayout.vue` inside `q-page-container`. Never add `
` to an SPA `App.vue` or page component. + +## 6. Do's and Don'ts + +### Do: + +- **Do** carry brand color through Quasar roles: `primary` Aggie Blue, `positive` Redwood, `negative` Merlot, `info`+`text-color="dark"` Tahoe, `warning`+`text-color="dark"` Gold, `secondary` Blue 70. +- **Do** reference tokens, not literals: `var(--q-primary)`, `var(--ucdavis-blue-100)`, `var(--ucdavis-gold-90)`. Route any new color through the canonical token files. +- **Do** use rem for spacing, sizing, and typography — never px. +- **Do** prefer Quasar utility classes over custom CSS, and combine selectors when rules overlap. +- **Do** use Proxima Nova for brand chrome and Roboto for the workspace, and keep them off each other's surfaces. +- **Do** keep gold as a thin accent (rule, ring, mark, arrow). Aggie Blue carries the surface. +- **Do** verify ≥4.5:1 body / ≥3:1 large-and-UI contrast; lean on the remapped greys (`grey-7`) and darkened gold text (#664d03). +- **Do** use `StatusBadge` and `StatusBanner` instead of raw `q-badge`/`q-banner` in SPAs, and set `dense` + `options-dense` on every select. +- **Do** give every dialog an accessible name and a close affordance, with Submit/Delete-only footers. +- **Do** keep exactly one `
` landmark per page. +- **Do** use `text-wrap: balance` on display/brand headings. + +### Don't: + +- **Don't** use inline `style=""`. Extract to a class. +- **Don't** use `!important` except for the documented Quasar contrast overrides (`.text-grey`, `.text-warning`, `tabs-no-fade`). +- **Don't** put bright gold (#ffbf00 / #ffc519) on white as text — it fails AA. Gold is a background and accent only. +- **Don't** let gold become a dominant surface; the secondary palette is "never dominant." +- **Don't** set in-app headings at display scale — workspace `h1`–`h3` are 1.2rem bold. Display type (>2.75rem) is brand-surface only. +- **Don't** add drop shadows to routine cards/panels; the system is flat by default. +- **Don't** nest cards, or reach for a card when a list or plain section is the better affordance. +- **Don't** hardcode hex in component or page CSS — derive from the brand tokens. +- **Don't** render two `
` landmarks on one page (one from layout + one from a component). +- **Don't** import consumer-SaaS tells: gradient text, glassmorphism, hero-metric templates, identical icon-card grids, or marketing buzzwords. This is institutional veterinary-school software. diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 000000000..e70c209a6 --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,53 @@ +# Product + +## Register + +product + +## Users + +The UC Davis Weill School of Veterinary Medicine community: faculty, clinicians, staff, and students. They authenticate through campus CAS and use VIPER as the internal hub for the school's operational systems — role and permission administration (RAPS), effort reporting, clinical scheduling, competency tracking (CTS), the directory, and CMS-managed content. + +These are mandatory, repeat users, not visitors. They arrive mid-workflow, often under time pressure, to get a specific administrative, academic, or clinical task done — the app is a tool, not a destination. Technical comfort ranges widely, from daily power users to occasional ones, and the same screens are used across desktop and mobile. + +## Product Purpose + +VIPER (2.0) is the School of Veterinary Medicine's internal web application suite: a single authenticated home, behind the UC Davis brand, for the operational systems the school runs on. It consolidates permission management, effort reporting, clinical scheduling, competency tracking, directory, and content into one consistent shell so staff and faculty don't juggle disconnected tools. + +Success looks like: tasks completed quickly and correctly with minimal training, consistent behavior across every area, and full accessibility for every user — on any device. The product earns trust by being reliable and clear, not by being novel. + +## Brand Personality + +Institutional, trustworthy, and efficient — the voice of a public university veterinary school. Three words: **authoritative, clear, accessible.** + +It should feel unmistakably like UC Davis: Aggie Blue and gold, calm and credible, brand-disciplined. Warmth comes from the school's mission — animal and public health, teaching, the people and patients — surfaced through brand imagery on welcome surfaces, not from decorative UI inside the workspace. The copy voice is plain, direct, and respectful of the user's time: say what a control does and what will happen. + +## Anti-references + +VIPER should **not** look like a consumer SaaS marketing site, a trendy startup dashboard, or a generic off-the-shelf admin template. Specifically avoid: + +- **Consumer-SaaS tells**: gradient text, glassmorphism, the hero-metric template (big number + small label + gradient accent), and identical icon-card grids. +- **Marketing buzzwords**: streamline, empower, supercharge, leverage, seamless, world-class, enterprise-grade, next-generation, game-changer. Use specific nouns and verbs for what the product literally does. +- **Off-brand color**: bright gold as a dominant surface or as text on white; dark-mode-with-neon-accents; anything that fights the UC Davis institutional palette. +- **Marketing-scale UI inside the app**: display-scale hero headings on working screens (those belong only to brand surfaces like the login splash). +- **Generic Material/Bootstrap admin look**: undifferentiated framework defaults with no brand discipline. + +## Design Principles + +1. **The tool gets out of the way.** Inside the app, density, speed, and legibility beat decoration; every element earns its place. The brand greets users at the door (the login splash); the workspace stays quiet. +2. **Accessibility is a floor, not a feature.** WCAG AA on every surface — contrast, landmarks, keyboard operation, screen-reader semantics — because the audience is mandatory daily users across a full range of needs. +3. **Brand discipline over novelty.** Carry the UC Davis identity faithfully: Aggie Blue carries the weight, gold accents but never dominates, consistency wins. The school's credibility is the design's job. +4. **Reuse over reinvention.** One Quasar component vocabulary, one token source, shared patterns (StatusBadge, StatusBanner, the dialog pattern). New screens compose existing parts rather than introducing one-offs. +5. **Correctness earns trust.** This is institutional software handling permissions, effort, and schedules. Clarity and predictability outrank cleverness; never surprise the user with what an action does. + +## Accessibility & Inclusion + +WCAG 2.1 AA is the system-wide standard, enforced rather than aspired to: + +- **Contrast**: ≥4.5:1 for body text, ≥3:1 for large text and UI. Quasar's mid-greys are remapped to AA-safe shades, bright gold is corrected before use as text, and inactive tab text is kept above the fade threshold. +- **Structure**: exactly one `
` landmark per page; a visible skip-to-content link. +- **Components**: every dialog has an accessible name and a close affordance; interactive non-buttons get full keyboard semantics (`enter`/`space`, `tabindex`, `role`, `aria-label`). +- **Announcements**: screen-reader live regions are polite by default and assertive only in direct response to a user action. +- **Motion & responsiveness**: reduced-motion preferences respected; layouts work from mobile (390px) up. Fonts are self-hosted for reliable rendering. + +Visual and component-level specifics live in [DESIGN.md](DESIGN.md). diff --git a/README.md b/README.md index 4ed94fb70..2301ec97c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ -# VIPER +# VIPER2 +[![Tests](https://github.com/ucdavis/VIPER/actions/workflows/tests.yml/badge.svg)](https://github.com/ucdavis/VIPER/actions/workflows/tests.yml) +[![Code Quality](https://github.com/ucdavis/VIPER/actions/workflows/code-quality.yml/badge.svg)](https://github.com/ucdavis/VIPER/actions/workflows/code-quality.yml) +[![CodeQL](https://github.com/ucdavis/VIPER/actions/workflows/codeql.yml/badge.svg)](https://github.com/ucdavis/VIPER/actions/workflows/codeql.yml) [![codecov](https://codecov.io/github/ucdavis/VIPER/graph/badge.svg?token=4Q8KQ3HHUF)](https://codecov.io/github/ucdavis/VIPER) Clinical, curriculum, and student management application for UC Davis School of Veterinary Medicine. @@ -8,7 +11,7 @@ Clinical, curriculum, and student management application for UC Davis School of - [.NET 10 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) - [Volta](https://volta.sh/) - Node.js version manager -- [Visual Studio 2022](https://visualstudio.microsoft.com/) or [VS Code](https://code.visualstudio.com/) +- [Visual Studio 2026](https://visualstudio.microsoft.com/) or [VS Code](https://code.visualstudio.com/) ### Node.js Setup with Volta (Recommended) @@ -142,6 +145,29 @@ VIPER2 features a sophisticated development setup with hot reload for both front - `vite-watch.js` - Debounced build script for development - `test/` - Unit tests +## AI Agent Tooling (Claude Code) + +This repo includes guidance files for AI coding assistants. Read these before +pointing an agent at the codebase: + +- `CLAUDE.md` - Primary project instructions: environment rules, architecture, + database/EF Core conventions, C# and Vue standards, security, and the + git/branch workflow. This is the source of truth. +- `AGENTS.md` - Entry point for non-Claude agents; defers to `CLAUDE.md`. +- `DESIGN.md` - The VIPER2 design system (UC Davis brand tokens, typography, + components, accessibility rules). Read before building or changing UI. +- `PRODUCT.md` - Product context: who uses VIPER2 and the design principles. + +Per-developer Claude config lives under `.claude/` and is gitignored, so install +the shared plugins yourself via the Claude Code `/plugin` command. The team uses: + +- **csharp-lsp** - C# language server, official marketplace (`anthropics/claude-plugins-official`) +- **vue** - Vue 3 skill (`github.com/harlan-zw/vue-ecosystem-skills`) +- **impeccable** - design and UI skill (`github.com/pbakaus/impeccable`) + +UI and API testing is driven through the Playwright MCP server (`@playwright/mcp`). +It is not committed to the repo, so add it to your own Claude Code MCP config. + ## IDE Setup ### Oxfmt Formatting (Automatic code formatting) @@ -166,9 +192,9 @@ The project includes VS Code launch configurations for debugging both frontend a 5. Set breakpoints in your TypeScript/Vue files and debug in VS Code **Backend Debugging (.NET):** -1. Use "`Attach to .NET Core`" configuration to attach to a running VIPER process +1. Use "`Attach to .NET Core`" configuration to attach to a running VIPER2 process 2. Use "`.NET Tests (Debug)`" to debug unit tests -3. The debugger will automatically find and attach to the running VIPER backend +3. The debugger will automatically find and attach to the running VIPER2 backend 4. **Note**: "Attach to .NET Core" may fail on the first attempt due to timing - simply try again and it will work ## Common Commands @@ -246,10 +272,47 @@ npm install ``` The hook will: -- Check C# code style with `dotnet format` on staged .cs files -- Run ESLint and TypeScript checks on staged Vue/TypeScript files (uses Node.js script for Windows compatibility) -- Only run on files you've actually changed -- Block commits if issues are found (bypass with `git commit --no-verify` if needed) +- Build the .NET projects first (required for tests and linting) +- Then run three checks in parallel, showing output only on failure: + - **Lint** on your staged files: ESLint/TypeScript on Vue/TS files and `dotnet format` style on `.cs` files + - **Test**: the full backend and frontend suites (`npm run test`) + - **Build verify**: `npm run verify:build` +- Block the commit if any check fails (bypass with `git commit --no-verify` if needed) + +Merge commits skip linting but still build, test, and verify. A rebase in +progress skips the pre-commit checks entirely. + +### Pre-push Hook (Jenkins Build Trigger) + +A Husky pre-push hook can trigger a Jenkins build automatically. It is installed +alongside the pre-commit hook when you run `npm install`, and it only fires when +you push to the `Development` branch. Pushes to any other branch are unaffected. + +The hook is opt-in. If no Jenkins credentials are configured it prints a warning +and exits cleanly, so it never blocks a push. + +**Setup:** + +1. Copy the environment template if you have not already: + ```sh + # Windows PowerShell + Copy-Item .env.local.example .env.local + + # Git Bash on Windows + cp .env.local.example .env.local + ``` +2. In `.env.local`, uncomment and fill the Jenkins block. Get your API token + from Jenkins under User menu > Configure > API Token: + ```env + JENKINS_USER=your-username + JENKINS_API_TOKEN=your-api-token + JENKINS_JOB_TOKEN=your-job-token + JENKINS_URL=https://your-jenkins-server/job/YourJob/buildWithParameters + ``` + +When you next push to `Development`, the hook waits about 10 seconds so the push +lands first, then triggers the parameterized Jenkins job in the background. +Credentials stay in `.env.local`, which is not tracked by git. ## Email Testing with Mailpit