Add admin dashboard project creation flow#295
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds an admin-dashboard “New ERP project” flow that can create an ERPNext-backed Project (optionally creating/associating a Customer, Contact, Address, Cost Center, and Activity Type), plus supporting ERPNext lookup endpoints and client helpers.
Changes:
- Extend the shared ERPNext client with generic CRUD helpers plus Customer/Contact/Address/Project creation utilities and cost center/customer/contact/user lookup helpers.
- Add new dashboard API endpoints for ERPNext lookups and for creating the full ERPNext project setup (with validation + audit event).
- Add a dashboard UI modal for creating projects, wiring it into the Projects view; rebuild the static dashboard bundle served by the API.
Reviewed changes
Copilot reviewed 8 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
packages/shared/src/five08/clients/erpnext.py |
Adds generic record CRUD + new helper methods for dashboard-driven ERPNext record creation and lookups. |
apps/api/src/five08/backend/api.py |
Adds request model, ERPNext lookup helpers, project-setup creation logic, and new dashboard API routes/handlers. |
apps/admin_dashboard/src/main.tsx |
Adds ERPNext lookup client calls and a “New project” modal/flow in the dashboard Projects view. |
tests/unit/test_erpnext_client.py |
Adds unit tests covering the new ERPNext client helper behaviors and payload shaping. |
tests/unit/test_backend_api.py |
Adds unit tests for new dashboard endpoints and ERPNext project setup orchestration. |
apps/api/src/five08/backend/static/dashboard/index.html |
Updates asset references for the rebuilt dashboard bundle. |
apps/api/src/five08/backend/static/dashboard/.vite/manifest.json |
Updates Vite manifest to the new built asset names. |
apps/api/src/five08/backend/static/dashboard/assets/index-cL6hQWRe.css |
New built CSS bundle output. |
apps/api/src/five08/backend/static/dashboard/assets/index-Cjc8hvil.css |
Removes prior built CSS bundle output. |
Files not reviewed (1)
- apps/api/src/five08/backend/static/dashboard/assets/index-cL6hQWRe.css: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "address_title": (address_title or normalized_customer).strip(), | ||
| "address_type": (address_type or "Billing").strip(), |
| useEffect(() => { | ||
| void onLoadCostCentersRef.current().then((options) => { | ||
| setCostCenters(options) | ||
| setCostCenter((current) => | ||
| options.some((option) => option.name === current) ? current : "Projects - 5", | ||
| ) | ||
| }) |
| useEffect(() => { | ||
| void onLoadCostCentersRef.current().then((options) => { | ||
| setCostCenters(options) | ||
| setCostCenter((current) => | ||
| options.some((option) => option.name === current) ? current : "Projects - 5", | ||
| ) | ||
| }) | ||
| }, []) | ||
|
|
||
| useEffect(() => { | ||
| if (customerMode !== "existing") return | ||
| const handle = window.setTimeout(() => { | ||
| void onSearchCustomersRef.current(customerQuery).then(setCustomerResults) | ||
| }, 250) | ||
| return () => window.clearTimeout(handle) | ||
| }, [customerMode, customerQuery]) | ||
|
|
||
| useEffect(() => { | ||
| if (customerMode !== "new") return | ||
| const handle = window.setTimeout(() => { | ||
| void onSearchAccountManagersRef.current(accountManagerQuery).then(setAccountManagerResults) | ||
| }, 250) | ||
| return () => window.clearTimeout(handle) | ||
| }, [customerMode, accountManagerQuery]) | ||
|
|
||
| useEffect(() => { | ||
| if (customerMode !== "new" || contactMode !== "existing") return | ||
| const handle = window.setTimeout(() => { | ||
| void onSearchContactsRef.current(contactQuery).then(setContactResults) | ||
| }, 250) | ||
| return () => window.clearTimeout(handle) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 019859158e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| customer_doc = client.create_customer( | ||
| customer_name=customer_name, | ||
| account_manager=account_manager, | ||
| default_currency=currency or "USD", | ||
| customer_details=customer_details, |
There was a problem hiding this comment.
Validate optional address/contact fields before creating Customer
This branch persists a new ERPNext Customer before checking address_line1_required and contact_first_name_required, so a malformed request (e.g., address city without line 1, or contact email without first name) returns 400 after already creating a Customer record. In production this leaves orphan Customer data even though the API reports validation failure.
Useful? React with 👍 / 👎.
| project_detail = client.create_project( | ||
| project_name=project_name, | ||
| customer=customer_id, | ||
| project_type="External", | ||
| default_cost_center=default_cost_center or "Projects - 5", |
There was a problem hiding this comment.
Prevent partial success when post-project steps fail
The project is created before ensure_activity_type and local cache refresh run, so if either later step fails (ERP permission/transient error or local cache write issue), the handler returns 502 even though the ERP project was already created. Users retrying after that error can create duplicate projects because the first write already succeeded.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6236244cb4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| activity_type_name = _text_or_none( | ||
| payload.activity_type | ||
| ) or _default_activity_type_for_project(project_name) | ||
| if len(activity_type_name) > 140: | ||
| raise ValueError("activity_type_too_long") |
There was a problem hiding this comment.
Cap default activity type to ERPNext field limit
project_name is allowed up to 140 characters, but when activity_type is omitted the code derives it as Engineering for {project_name} and then enforces a 140-character cap on that derived value. This makes otherwise valid project names (roughly 125–140 chars) fail with activity_type_too_long unless the caller manually overrides activity type, which is an unexpected validation failure for normal requests.
Useful? React with 👍 / 👎.
…ect-creation # Conflicts: # apps/api/src/five08/backend/static/dashboard/.vite/manifest.json # apps/api/src/five08/backend/static/dashboard/index.html
| async function submit() { | ||
| if (!canSubmit) return | ||
| const effectiveActivityType = activityTypeEdited | ||
| ? activityType.trim() | ||
| : defaultActivityType.trim() | ||
| const success = await props.onCreateProject({ | ||
| project_name: projectName.trim(), | ||
| customer_mode: customerMode, | ||
| customer_name: customerMode === "new" ? customerName.trim() : undefined, | ||
| customer: customerMode === "existing" ? selectedCustomer.trim() : undefined, | ||
| account_manager: customerMode === "new" ? accountManager.trim() || undefined : undefined, | ||
| default_billing_currency: customerMode === "new" ? currency.trim() || "USD" : undefined, | ||
| default_cost_center: costCenter.trim() || "Projects - 5", | ||
| activity_type: effectiveActivityType || undefined, | ||
| customer_details: customerMode === "new" ? customerDetails.trim() || undefined : undefined, |
| New ERP project | ||
| </strong> | ||
| <span className="text-sm text-muted-foreground"> | ||
| Creates a project and associated customer. |
| def test_dashboard_search_erpnext_customers_requires_project_write( | ||
| client: TestClient, | ||
| ) -> None: | ||
| session = api.AuthSession( | ||
| subject="steering-1", | ||
| email="steering@508.dev", | ||
| display_name="Steering User", | ||
| groups=["Steering Committee"], |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1a20b48ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if project_id: | ||
| return self.get_project(project_id) |
There was a problem hiding this comment.
Return created project when detail fetch fails
After create_record("Project", payload) succeeds, this method unconditionally performs a second GET and propagates any failure from that read. In transient ERP/API failures where the POST succeeded but the follow-up GET fails, the dashboard request returns 502 even though the project was already created, so operator retries can create duplicate ERPNext projects. The create path should treat the POST response as success (or degrade/fallback) instead of failing the whole operation on a non-critical read-after-write.
Useful? React with 👍 / 👎.
| "ERPNext Project was created but local cache refresh failed project=%s", | ||
| erpnext_project_id, | ||
| ) | ||
| cache_refresh_error = str(exc) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff1154e814
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return [] | ||
| client = _erpnext_client() | ||
| try: | ||
| users = client.search_users(normalized_query, limit=20) |
There was a problem hiding this comment.
Filter account managers before applying result limit
The search limits ERPNext users to 20 before restricting to enabled @508.dev accounts, so valid account managers can be dropped whenever the first 20 matches are mostly external or disabled users. In that case the UI shows no eligible managers even though they exist, which blocks or misleads project setup in larger user directories. Push the domain/enabled constraints into the ERPNext query (or increase/paginate before filtering) so limiting is applied to the eligible set.
Useful? React with 👍 / 👎.
Summary
Details
Engineering for {{project_name}}unless overridden in Advanced.Projects - 5.@508.devERPNext Users; backend rejects non-508 email values.Validation
uv run pytest tests/unit/test_erpnext_client.py tests/unit/test_backend_api.py -q./scripts/lint.sh./scripts/mypy.shbun run lint && bun run typecheck && bun run testbun run build