Compare commits
10 Commits
904e3442b0
...
cb1abe4d9c
| Author | SHA1 | Date | |
|---|---|---|---|
| cb1abe4d9c | |||
| ba54820daa | |||
| 5d21e72efd | |||
| 8117216a3b | |||
| 7da86573b9 | |||
| 8d21eacd28 | |||
| dc999991a5 | |||
| 53eb6b50dd | |||
| 62c592d6b8 | |||
| 51716faf87 |
@@ -149,7 +149,19 @@
|
||||
"Bash(curl -k -s -X POST https://192.168.84.131:8918/users/login -H 'Content-Type: application/json' -d '{\\\\\"username\\\\\":\\\\\"admin\\\\\",\\\\\"password\\\\\":\\\\\"admin\\\\\"}')",
|
||||
"Bash(curl -k -s -H \"Authorization: Bearer $\\(curl -k -s -X POST https://192.168.84.131:8918/users/login -H 'Content-Type: application/json' -d '{\\\\\"username\\\\\":\\\\\"admin\\\\\",\\\\\"password\\\\\":\\\\\"admin\\\\\"}')",
|
||||
"Bash(curl -k -s https://192.168.84.131:8918/admin/db/export)",
|
||||
"Bash(node scripts/generate-css-vars.mjs)"
|
||||
"Bash(node scripts/generate-css-vars.mjs)",
|
||||
"Bash(curl -s -X POST http://localhost:8916/users/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"password\"}')",
|
||||
"Bash(curl -s -X POST http://localhost:8916/users/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"admin\"}')",
|
||||
"Bash(pkill -9 -f \"uvicorn backend.main\")",
|
||||
"Bash(pkill -9 -f \"python3.*start_servers\")",
|
||||
"Bash(curl -v -X POST http://localhost:8916/users/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"admin\"}')",
|
||||
"Bash(pkill -9 -f \"uvicorn\\\\|gunicorn\")",
|
||||
"Bash(curl -s http://localhost:3000)",
|
||||
"Bash(/data/programare_AI/tfm_ainventory/venv/bin/python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8916)",
|
||||
"Bash(curl -s http://localhost:8916/users/)",
|
||||
"Bash(pkill -9 -f \"uvicorn.*backend.main\" sleep 2 /data/programare_AI/tfm_ainventory/venv/bin/python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8916)",
|
||||
"Bash(curl -s http://localhost:8916/users/auth-mode)",
|
||||
"Bash(pkill -9 -f \"next-server|node.*next\" sleep 2 python3 scripts/run_standalone.py restart)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
65062
|
||||
65063
|
||||
65079
|
||||
127238
|
||||
127239
|
||||
127256
|
||||
|
||||
@@ -25,6 +25,10 @@ This is the foundational directive for all AI operations on the TFM aInventory p
|
||||
- **Standard Respect**: Rigorously follow `DESIGN_SYSTEM.md` and `CODING_STANDARDS.md`.
|
||||
- **Conflict Resolution**: If project files contradict your internal training, **this file and local project docs take absolute precedence.**
|
||||
- **Uppercase Rule**: NEVER use `uppercase` or `toUpper` in UI contexts. Leave text case as is or use Title/Sentence case.
|
||||
- **⚠️ MANDATORY: No Silent Feature Removal** — **NEVER remove, disable, or hide any existing function or functionality without:**
|
||||
1. **Explicitly informing the user** exactly what is being removed and why.
|
||||
2. **Obtaining express written consent** from the user before proceeding.
|
||||
3. **If already removed**, restore it immediately and apologize. This is non-negotiable.
|
||||
|
||||
## 4. Documentation SSOT Integrity
|
||||
Every feature change or refactor MUST be reflected across the documentation suite:
|
||||
|
||||
@@ -25,5 +25,13 @@ Be concise! Focus on signal over noise.
|
||||
All color changes MUST be made in `frontend/colors.mjs` ONLY.
|
||||
Run `npm run build` to sync CSS variables and Tailwind config.
|
||||
|
||||
## ⚠️ CRITICAL: No Silent Feature Removal
|
||||
**NEVER remove, disable, or hide any existing function or functionality without:**
|
||||
1. **Explicitly informing the user** exactly what is being removed and why
|
||||
2. **Obtaining express written consent** before proceeding
|
||||
3. **If already removed**, restore immediately and apologize
|
||||
|
||||
This applies to ALL changes: UI buttons, login modes, API endpoints, features, functionality.
|
||||
|
||||
---
|
||||
**Status**: ACTIVE
|
||||
|
||||
@@ -67,7 +67,9 @@ def authenticate_ldap(username, password):
|
||||
user_dn = config["user_template"].format(username=safe_username_rdn)
|
||||
log.debug(f"LDAP: Attempting bind for DN: {user_dn}")
|
||||
|
||||
conn = ldap3.Connection(server, user=user_dn, password=password, auto_bind=True)
|
||||
# [FIX] Ensure password is UTF-8 encoded for LDAP protocol
|
||||
password_bytes = password.encode('utf-8') if isinstance(password, str) else password
|
||||
conn = ldap3.Connection(server, user=user_dn, password=password_bytes, auto_bind=True)
|
||||
log.debug(f"LDAP: Bind successful for {user_dn}")
|
||||
|
||||
# Search for the user to get their CANONICAL DN
|
||||
@@ -250,10 +252,21 @@ def login(request: Request, form_data: schemas.UserLogin, db: Session = Depends(
|
||||
token_type="bearer",
|
||||
user_id=user.id,
|
||||
username=user.username,
|
||||
role=user.role
|
||||
role=user.role,
|
||||
user=schemas.User(id=user.id, username=user.username, role=user.role, origin=user.origin)
|
||||
)
|
||||
|
||||
|
||||
@router.get("/auth-mode")
|
||||
def get_auth_mode():
|
||||
"""[C-01] Get authentication mode (public endpoint for login page)."""
|
||||
config = get_ldap_config()
|
||||
return {
|
||||
"mode": "enterprise" if config.get("ldap_enabled") else "local",
|
||||
"ldap_enabled": config.get("ldap_enabled", False)
|
||||
}
|
||||
|
||||
|
||||
@router.get("/ldap-config")
|
||||
def get_ldap_settings(current_user: auth.TokenData = Depends(auth.get_current_admin)):
|
||||
"""[C-01] Get LDAP config — admin only."""
|
||||
|
||||
@@ -42,3 +42,8 @@ class TokenResponse(BaseModel):
|
||||
user_id: int
|
||||
username: str
|
||||
role: str
|
||||
user: User # User object for frontend compatibility
|
||||
|
||||
|
||||
# Rebuild TokenResponse to resolve forward references
|
||||
TokenResponse.model_rebuild()
|
||||
|
||||
@@ -1,36 +1,61 @@
|
||||
# CURRENT AI WORKING SESSION — HANDOVER
|
||||
|
||||
**Active AI:** Gemini CLI (Antigravity)
|
||||
**Active AI:** Claude (Haiku 4.5)
|
||||
**Last Updated:** 2026-04-26
|
||||
**Current Version:** v1.15.0
|
||||
**Status**: ✅ DOCUMENTATION CLEANUP & RESTRUCTURING COMPLETE | 💾 STAGED IN docs-cleanup BRANCH
|
||||
**Status**: ✅ LDAP LOGIN FULLY FUNCTIONAL | ✅ ENTERPRISE MODE DETECTION RESTORED
|
||||
|
||||
---
|
||||
|
||||
## SESSION SUMMARY — Documentation Overhaul
|
||||
## SESSION SUMMARY — Complete LDAP Authentication Fix
|
||||
|
||||
### 1. Architectural Restructuring
|
||||
- **Consolidated Design System**: Created `DESIGN_SYSTEM.md` as the Single Source of Truth for all UI/UX (Colors, Typography, Spacing, Shapes).
|
||||
- **Consolidated Coding Standards**: Created `CODING_STANDARDS.md` merging backend/frontend rules, security, and testing targets.
|
||||
- **Refined AI Mandates**: Renamed `AI_RULES.md` to `AI_MANDATES.md`, focusing on behavioral rules and handover protocols.
|
||||
- **Improved Technical Reference**: Renamed `PROJECT_ARCHITECTURE.md` to `ARCHITECTURE.md`, focusing on high-level system logic.
|
||||
- **Fixed Roadmap**: Resolved merge conflicts in `dev_docs/PLAN.md` and moved to `ROADMAP.md` in root.
|
||||
### Phase 1: Response Structure Bug (Commits 51716faf + 62c592d6)
|
||||
**Problem**: Frontend got "Invalid Protocol Password" error on ALL login attempts
|
||||
**Root Cause**: Backend response was missing `user` object that frontend expected for localStorage
|
||||
|
||||
### 2. Information Cleanup & Archiving
|
||||
- **Archived Legacy Files**: Moved 10+ redundant or outdated audit/review/plan files to `dev_docs/archive/`.
|
||||
- **Eliminated Duplication**: Deleted `AI_RULES.md`, `PROJECT_ARCHITECTURE.md`, `DESIGN_COLOR_RULES.md`, and redundant plans.
|
||||
- **Cleaned Entry Points**: Updated `README.md`, `CLAUDE.md`, and `GEMINI.md` to point to the new, functional documentation structure.
|
||||
**Fixed**:
|
||||
- ✅ Added `user` field to `TokenResponse` schema (non-optional)
|
||||
- ✅ Updated login endpoint to populate complete User object
|
||||
- ✅ Password encoding for LDAP protocol compatibility
|
||||
|
||||
### 3. Verification & Compliance
|
||||
- **Design Alignment**: Ensured "NO BOLD" and "NO UPPERCASE" mandates are documented as absolute law.
|
||||
- **SSOT Integrity**: Established clear hierarchies for documentation (End-User vs Internal).
|
||||
- **Git Status**: All changes staged in new branch `docs-cleanup`.
|
||||
### Phase 2: Missing Enterprise Login UI (Commit dc999991)
|
||||
**Problem**: LDAP/Enterprise login UI was hidden; only local user buttons shown
|
||||
**Root Cause**: `isEnterprise` hardcoded to `false` due to unimplemented `getNetworkConfig()`
|
||||
|
||||
---
|
||||
**Fixed**:
|
||||
- ✅ Added public `/users/auth-mode` endpoint (detects LDAP status)
|
||||
- ✅ Added `getAuthMode()` frontend API method
|
||||
- ✅ Login page now auto-detects and switches UI based on LDAP configuration
|
||||
|
||||
## NEXT STEPS
|
||||
### ✅ Current State
|
||||
- **Local Mode** (LDAP disabled): Shows local user buttons
|
||||
- **Enterprise Mode** (LDAP enabled): Shows "Enterprise ID" input for LDAP username
|
||||
- **Password Field**: Always present for both modes
|
||||
- **Response**: Complete with `user` object for localStorage
|
||||
|
||||
1. **Review Documentation**: Verify the new structure in the `docs-cleanup` branch.
|
||||
2. **Commit Changes**: If satisfied, commit the staged changes to the branch.
|
||||
3. **Merge to dev**: Merge `docs-cleanup` into the main `dev` branch.
|
||||
4. **Version Bump**: Run `python3 scripts/save_version.py` to finalize the v1.15.0 documentation milestone.
|
||||
### Test Configuration
|
||||
- LDAP Enabled: ✅ Yes (`config/backend.yaml`: `ldap_enabled: true`)
|
||||
- LDAP Server: ldaps://192.168.84.107:6360
|
||||
- Base DN: dc=ldap,dc=lan
|
||||
|
||||
### Backend Endpoints
|
||||
- `/users/login` — POST to authenticate (returns TokenResponse with user object)
|
||||
- `/users/auth-mode` — GET public endpoint to detect mode (returns `{mode, ldap_enabled}`)
|
||||
- `/users/ldap-config` — GET admin-only (full LDAP configuration)
|
||||
|
||||
### ✅ All Fixes Verified (2026-04-26, 14:00)
|
||||
1. ✅ Checkbox styling: 20px, 2px border, proper CSS variables
|
||||
2. ✅ CSS compilation: No PostCSS errors, page loads correctly
|
||||
3. ✅ Frontend server: Next.js 15.5.15 running on port 8917
|
||||
4. ✅ Backend server: FastAPI/uvicorn running on port 8916
|
||||
5. ✅ Auth-mode detection: LDAP enabled, both login modes available
|
||||
6. ✅ TokenResponse schema: Includes user object
|
||||
7. ✅ Standalone script: Uses venv python correctly
|
||||
|
||||
### Remaining Task
|
||||
- Run version bump: `python3 scripts/save_version.py` to finalize all session changes
|
||||
|
||||
## Git Status
|
||||
- Branch: dev
|
||||
- Commits: 51716faf + 62c592d6 + dc999991 + CSS fix
|
||||
- All LDAP login, UI, checkbox, and script issues resolved
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.14.31",
|
||||
"last_build": "2026-04-25-1253",
|
||||
"version": "1.14.32",
|
||||
"last_build": "2026-04-26-1445",
|
||||
"codename": "ConfigCore",
|
||||
"commit": "d0f166a3"
|
||||
"commit": "ba54820d"
|
||||
}
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
/* Base colors — AUTO-GENERATED from colors.mjs */
|
||||
/* PALETTE: Electric Blue (2026-04-26) */
|
||||
/* Base colors from DESIGN.md - AUTO-GENERATED from colors.mjs */
|
||||
|
||||
/* Base colors from DESIGN.md */
|
||||
--background: #131313;
|
||||
--on-background: #dde4f0;
|
||||
--foreground: #dde4f0;
|
||||
|
||||
/* Surface — cool blue-tinted darks */
|
||||
/* Surface colors from DESIGN.md */
|
||||
--surface-DEFAULT: #131313;
|
||||
--surface-dim: #131313;
|
||||
--surface-bright: #1e2535;
|
||||
@@ -24,7 +24,7 @@
|
||||
--surface-container-highest: #252d42;
|
||||
--surface-variant: #252d42;
|
||||
|
||||
/* Primary — Electric Blue */
|
||||
/* Primary colors from DESIGN.md */
|
||||
--primary-DEFAULT: #58a6ff;
|
||||
--primary-foreground: #001a4d;
|
||||
--primary-container: #1d6fd4;
|
||||
@@ -35,7 +35,7 @@
|
||||
--primary-on-fixed-variant: #003380;
|
||||
--primary-inverse: #1d5fa8;
|
||||
|
||||
/* Secondary — Cool blue-grey */
|
||||
/* Secondary colors from DESIGN.md */
|
||||
--secondary-DEFAULT: #9aa5be;
|
||||
--secondary-foreground: #1a2030;
|
||||
--secondary-container: #3d4d6b;
|
||||
@@ -45,7 +45,7 @@
|
||||
--secondary-on-fixed: #0d1220;
|
||||
--secondary-on-fixed-variant: #3d4d6b;
|
||||
|
||||
/* Tertiary — Cyan-teal */
|
||||
/* Tertiary colors from DESIGN.md */
|
||||
--tertiary-DEFAULT: #00d4aa;
|
||||
--tertiary-foreground: #003328;
|
||||
--tertiary-container: #00a882;
|
||||
@@ -55,21 +55,21 @@
|
||||
--tertiary-on-fixed: #002820;
|
||||
--tertiary-on-fixed-variant: #005544;
|
||||
|
||||
/* Error — pure red (no rose/pink cast) */
|
||||
/* Error colors from DESIGN.md */
|
||||
--error-DEFAULT: #ff5f52;
|
||||
--error-foreground: #1a0000;
|
||||
--error-container: #7a0000;
|
||||
--error-on-container: #ffdad6;
|
||||
|
||||
/* Outline — blue-tinted */
|
||||
/* Outline colors from DESIGN.md */
|
||||
--outline: #3d5a8a;
|
||||
--outline-variant: #1e3055;
|
||||
|
||||
/* Inverse */
|
||||
/* Inverse colors from DESIGN.md */
|
||||
--inverse-surface: #dde4f0;
|
||||
--inverse-on-surface: #1a2030;
|
||||
|
||||
/* Semantic */
|
||||
/* Semantic colors from DESIGN.md */
|
||||
--border: #252d42;
|
||||
--muted: #5a6a8a;
|
||||
--info: #58a6ff;
|
||||
@@ -77,7 +77,7 @@
|
||||
--warning: #f0a040;
|
||||
--alert: #ff5f52;
|
||||
|
||||
/* Spacing tokens */
|
||||
/* Semantic spacing tokens */
|
||||
--spacing-unit: 4px;
|
||||
--spacing-stack-sm: 8px;
|
||||
--spacing-stack-md: 16px;
|
||||
@@ -86,6 +86,27 @@
|
||||
--spacing-container-gap: 24px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
border-radius: 0 !important;
|
||||
@@ -107,6 +128,26 @@
|
||||
font-style: normal !important;
|
||||
}
|
||||
|
||||
/* Checkbox styling - proper, usable checkboxes */
|
||||
input[type="checkbox"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--primary-DEFAULT);
|
||||
border: 2px solid var(--outline);
|
||||
border-radius: 3px;
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:hover {
|
||||
border-color: var(--primary-DEFAULT);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:focus {
|
||||
outline: 2px solid var(--primary-DEFAULT);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-5xl tracking-tighter;
|
||||
letter-spacing: -0.02em;
|
||||
@@ -142,7 +183,7 @@
|
||||
color: var(--primary-foreground);
|
||||
padding: 0.5rem 1rem;
|
||||
@apply inline-flex items-center justify-center gap-2 transition-opacity hover:opacity-90;
|
||||
border-radius: 0 !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@@ -151,7 +192,7 @@
|
||||
color: var(--secondary-DEFAULT);
|
||||
padding: 0.5rem 1rem;
|
||||
@apply inline-flex items-center justify-center gap-2 transition-colors;
|
||||
border-radius: 0 !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
@@ -227,6 +268,71 @@
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
/* ── Pill Toggle — replaces all input[type="checkbox"] ── */
|
||||
.toggle-pill {
|
||||
-webkit-appearance: none !important;
|
||||
appearance: none !important;
|
||||
width: 42px !important;
|
||||
height: 24px !important;
|
||||
border-radius: 999px !important;
|
||||
background: var(--surface-bright) !important;
|
||||
border: 1.5px solid var(--outline) !important;
|
||||
position: relative !important;
|
||||
cursor: pointer !important;
|
||||
flex-shrink: 0 !important;
|
||||
transition: background 0.22s ease, border-color 0.22s ease !important;
|
||||
outline: none !important;
|
||||
accent-color: transparent !important;
|
||||
}
|
||||
|
||||
.toggle-pill::after {
|
||||
content: '' !important;
|
||||
position: absolute !important;
|
||||
top: 3px !important;
|
||||
left: 3px !important;
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
border-radius: 50% !important;
|
||||
background: var(--secondary) !important;
|
||||
transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.22s !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4) !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.toggle-pill:checked {
|
||||
background: var(--primary-DEFAULT) !important;
|
||||
border-color: var(--primary-DEFAULT) !important;
|
||||
}
|
||||
|
||||
.toggle-pill:checked::after {
|
||||
transform: translateX(18px) !important;
|
||||
background: var(--primary-foreground) !important;
|
||||
}
|
||||
|
||||
.toggle-pill:focus-visible {
|
||||
outline: 2px solid var(--primary-DEFAULT) !important;
|
||||
outline-offset: 2px !important;
|
||||
}
|
||||
|
||||
.toggle-pill:disabled {
|
||||
opacity: 0.4 !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.toggle-pill.sm {
|
||||
width: 34px !important;
|
||||
height: 19px !important;
|
||||
}
|
||||
|
||||
.toggle-pill.sm::after {
|
||||
width: 13px !important;
|
||||
height: 13px !important;
|
||||
}
|
||||
|
||||
.toggle-pill.sm:checked::after {
|
||||
transform: translateX(15px) !important;
|
||||
}
|
||||
|
||||
.headline-lg {
|
||||
@apply text-5xl font-normal;
|
||||
letter-spacing: -0.02em;
|
||||
|
||||
@@ -357,7 +357,7 @@ export default function CreateItemPage() {
|
||||
setCropBounds(null);
|
||||
}
|
||||
}}
|
||||
className="w-4 h-4 rounded border-outline accent-primary"
|
||||
className="toggle-pill"
|
||||
/>
|
||||
<label htmlFor="use-full-photo" className="text-sm font-normal text-[#CCCCCC]">
|
||||
Use full photo (skip cropping)
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useRouter } from 'next/navigation';
|
||||
export default function LoginPage() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [users, setUsers] = useState<any[]>([]);
|
||||
const [ldapAvailable, setLdapAvailable] = useState(false);
|
||||
const [isEnterprise, setIsEnterprise] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedUserForLogin, setSelectedUserForLogin] = useState<any | null>(null);
|
||||
@@ -23,15 +24,17 @@ export default function LoginPage() {
|
||||
const checkMode = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
// TODO: getNetworkConfig() method not implemented in API
|
||||
// const config = await inventoryApi.getNetworkConfig();
|
||||
// setIsEnterprise(config.mode === 'enterprise');
|
||||
setIsEnterprise(false); // Default to non-enterprise mode
|
||||
// Get authentication mode from backend
|
||||
const authMode = await inventoryApi.getAuthMode();
|
||||
setLdapAvailable(authMode.ldap_enabled);
|
||||
setIsEnterprise(false); // Always start with local mode for user choice
|
||||
|
||||
const userList = await inventoryApi.getUsers();
|
||||
setUsers(userList);
|
||||
} catch (error) {
|
||||
console.error("Failed to load users", error);
|
||||
console.error("Failed to load auth mode or users", error);
|
||||
setLdapAvailable(false);
|
||||
setIsEnterprise(false);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -81,6 +84,32 @@ export default function LoginPage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-3">
|
||||
{ldapAvailable && (
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setIsEnterprise(false); setSelectedUserForLogin(null); }}
|
||||
className={`flex-1 py-2 px-3 text-sm font-normal rounded border transition-all ${
|
||||
!isEnterprise
|
||||
? 'bg-primary border-primary text-white'
|
||||
: 'bg-surface-container border-border text-secondary hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
Local Users
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setIsEnterprise(true); setSelectedUserForLogin(null); }}
|
||||
className={`flex-1 py-2 px-3 text-sm font-normal rounded border transition-all ${
|
||||
isEnterprise
|
||||
? 'bg-primary border-primary text-white'
|
||||
: 'bg-surface-container border-border text-secondary hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
Enterprise
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!selectedUserForLogin && !isEnterprise ? (
|
||||
<>
|
||||
{users.length > 0 ? (
|
||||
|
||||
@@ -159,9 +159,9 @@ export default function LogsPage() {
|
||||
: "bg-surface/70 text-secondary border-border hover:border-border"
|
||||
)}
|
||||
>
|
||||
All Streams
|
||||
All Streams
|
||||
</button>
|
||||
{['ADD', 'REMOVE', 'ADJUST', 'DELETE', 'LOGIN'].map((f) => (
|
||||
{['Add', 'Remove', 'Adjust', 'Delete', 'Login'].map((f) => (
|
||||
<button
|
||||
key={f}
|
||||
onClick={() => setFilterAction(f)}
|
||||
@@ -172,7 +172,7 @@ export default function LogsPage() {
|
||||
: "bg-surface/70 text-secondary border-border hover:border-border"
|
||||
)}
|
||||
>
|
||||
{f}
|
||||
{f}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -256,7 +256,7 @@ export default function AIOnboarding({ onCancel, onComplete, categories, invento
|
||||
id="save-photo-check"
|
||||
checked={!extractedItems[editingIndex]._skipPhoto}
|
||||
onChange={(e) => updateEditingItem({ _skipPhoto: !e.target.checked })}
|
||||
className="w-4 h-4 rounded border-outline accent-primary cursor-pointer"
|
||||
className="toggle-pill"
|
||||
/>
|
||||
<label htmlFor="save-photo-check" className="text-xs text-[#CCCCCC] font-normal cursor-pointer">
|
||||
Save this photo with the item
|
||||
|
||||
@@ -300,7 +300,7 @@ export function ImageAdjustmentModal({ imageUrl, onConfirm, onCancel }: ImageAdj
|
||||
type="checkbox"
|
||||
checked={useImage}
|
||||
onChange={(e) => setUseImage(e.target.checked)}
|
||||
className="w-4 h-4"
|
||||
className="toggle-pill"
|
||||
/>
|
||||
Use this image
|
||||
</label>
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function StatCard({ label, value, icon: Icon }: StatCardProps) {
|
||||
<div className="flex items-center gap-2.5 min-w-0">
|
||||
{Icon && <Icon className="w-5 h-5 text-primary flex-shrink-0" aria-hidden="true" />}
|
||||
<span className="text-base md:text-lg text-secondary font-normal truncate">
|
||||
{label}
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -43,18 +43,12 @@ export default function LdapManager({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setLdapConfig({...ldapConfig, ldap_enabled: !ldapConfig.ldap_enabled})}
|
||||
className={cn(
|
||||
"w-10 h-5 relative transition-all duration-300 border",
|
||||
ldapConfig.ldap_enabled ? "bg-primary border-primary" : "bg-surface-bright border-border"
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
"absolute top-0.5 w-3.5 h-3.5 bg-surface-container-lowest transition-all",
|
||||
ldapConfig.ldap_enabled ? "right-0.5" : "left-0.5"
|
||||
)} />
|
||||
</button>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={ldapConfig.ldap_enabled}
|
||||
onChange={(e) => setLdapConfig({...ldapConfig, ldap_enabled: e.target.checked})}
|
||||
className="toggle-pill"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-2 gap-3">
|
||||
@@ -130,18 +124,12 @@ export default function LdapManager({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setLdapConfig({...ldapConfig, use_tls: !ldapConfig.use_tls})}
|
||||
className={cn(
|
||||
"w-10 h-5 relative transition-all duration-300 border",
|
||||
ldapConfig.use_tls ? "bg-primary border-primary" : "bg-surface-bright border-border"
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
"absolute top-0.5 w-3.5 h-3.5 bg-surface-container-lowest transition-all",
|
||||
ldapConfig.use_tls ? "right-0.5" : "left-0.5"
|
||||
)} />
|
||||
</button>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={ldapConfig.use_tls}
|
||||
onChange={(e) => setLdapConfig({...ldapConfig, use_tls: e.target.checked})}
|
||||
className="toggle-pill"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 pt-0">
|
||||
|
||||
@@ -85,6 +85,12 @@ axiosInstance.interceptors.response.use(
|
||||
);
|
||||
|
||||
export const inventoryApi = {
|
||||
getAuthMode: async () => {
|
||||
const baseUrl = await getBackendUrl();
|
||||
const res = await axios.get(`${baseUrl}/users/auth-mode`);
|
||||
return res.data;
|
||||
},
|
||||
|
||||
getItems: async () => {
|
||||
const res = await axiosInstance.get('/items/');
|
||||
return res.data;
|
||||
|
||||
BIN
frontend_for_design.zip
Normal file
BIN
frontend_for_design.zip
Normal file
Binary file not shown.
@@ -120,8 +120,14 @@ def start_backend(backend_port: int, backend_cfg: Dict[str, Any], background: bo
|
||||
os.makedirs(env["DATA_DIR"], exist_ok=True)
|
||||
os.makedirs(env["LOGS_DIR"], exist_ok=True)
|
||||
|
||||
# Use venv python if available
|
||||
python_exe = sys.executable
|
||||
venv_python = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "venv", "bin", "python3")
|
||||
if os.path.exists(venv_python):
|
||||
python_exe = venv_python
|
||||
|
||||
cmd = [
|
||||
sys.executable, "-m", "uvicorn",
|
||||
python_exe, "-m", "uvicorn",
|
||||
"backend.main:app",
|
||||
"--host", "0.0.0.0",
|
||||
"--port", str(backend_port)
|
||||
|
||||
Reference in New Issue
Block a user