Workspace white screen error resolved, issue was when we migrated from the local SQLite database to the Client-Server architecture during Phase 2, we didn't implement backend endpoints for adding, updating, or deleting users. Because the app.jsx file had nowhere to send those requests, the buttons didn't do anything
This commit is contained in:
+9
-4
@@ -41,13 +41,18 @@
|
||||
|
||||
## ⏭️ Upcoming Steps
|
||||
|
||||
### Phase 2: Frontend Refactor (✅ Completed)
|
||||
1. **API Service:** Created `api.js` to handle all network requests to the FastAPI backend.
|
||||
### Phase 2: Frontend Refactor & Workflow Polish (✅ Completed)
|
||||
1. **API Service:** Created `api.js` to dynamically connect to the backend (resolved connection refused issues by using dynamic hostnames) and handle all network requests.
|
||||
2. **Authentication Hook:** Updated the Login screen to use real JWT tokens.
|
||||
3. **Component Updates:**
|
||||
- Swapped `DashyDB` calls for `async` API calls in `app.jsx`.
|
||||
- Implemented "Loading" states for UI responsiveness during network calls.
|
||||
4. **Cleanup:** Archived `db.js`, `data.jsx`, and removed `sql.js` WASM dependency from `Dashy.html`.
|
||||
- Fixed UI state refresh issues by silently loading subsequent data updates.
|
||||
4. **Task Workflows:**
|
||||
- **Completion:** Added a "Mark as completed" button in `TaskDetail` that changes task status to `closed`, records it in the Audit Log, and removes the task from the main Overview board.
|
||||
- **Reopening:** Added a "Reopen task" button to restore accidentally closed tasks back to the queue.
|
||||
- **User Views:** Updated `UserScreen` to accurately display open task counts and render a dedicated, faded "Completed" section at the bottom for closed tasks.
|
||||
- **Audit Rendering:** Fixed crash in `TaskDetail` by passing global API audit logs and filtering them locally for individual tasks.
|
||||
5. **Cleanup:** Archived `db.js`, `data.jsx` to `Dashy-v1/scraps/` and removed `sql.js` WASM dependency from `Dashy.html`.
|
||||
|
||||
### Phase 3: Advanced Features
|
||||
- **Real-time Notifications:** Explore WebSockets for task assignments.
|
||||
|
||||
@@ -66,6 +66,32 @@ class ApiService {
|
||||
return this.request('/users');
|
||||
}
|
||||
|
||||
async createUser(userData) {
|
||||
const data = await this.request('/users', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(userData),
|
||||
});
|
||||
this.notify();
|
||||
return data;
|
||||
}
|
||||
|
||||
async updateUser(id, updates) {
|
||||
const data = await this.request(`/users/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(updates),
|
||||
});
|
||||
this.notify();
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteUser(id) {
|
||||
const data = await this.request(`/users/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
this.notify();
|
||||
return data;
|
||||
}
|
||||
|
||||
async getTasks() {
|
||||
return this.request('/tasks');
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ function App() {
|
||||
setAuthed(false);
|
||||
setShowSettings(false);
|
||||
}}
|
||||
onSwitchUser={async (id) => {
|
||||
onSwitchUser={async (id) => {
|
||||
try {
|
||||
await api.login(id, "password123");
|
||||
setMeId(id);
|
||||
@@ -244,6 +244,43 @@ function App() {
|
||||
alert("Failed to switch user");
|
||||
}
|
||||
}}
|
||||
onCreateUser={async (u) => {
|
||||
try {
|
||||
const id = u.name.split(' ')[0].toLowerCase() + Math.floor(Math.random()*100);
|
||||
await api.createUser({
|
||||
id,
|
||||
name: u.name,
|
||||
role: u.role,
|
||||
hue: Math.floor(Math.random() * 360),
|
||||
initials: u.name.split(' ').map(s=>s[0]).join('').slice(0,2).toUpperCase(),
|
||||
account_type: u.account_type,
|
||||
password: "password123"
|
||||
});
|
||||
await api.addAudit({ actor: meId, action: 'user_created', summary: 'Added ' + u.name + ' (' + (u.account_type||'standard') + ')', target: id });
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
alert("Failed to create user: " + e.message);
|
||||
}
|
||||
}}
|
||||
onDeleteUser={async (id) => {
|
||||
try {
|
||||
const u = userMap[id];
|
||||
await api.deleteUser(id);
|
||||
await api.addAudit({ actor: meId, action: 'user_deleted', summary: 'Removed ' + (u?u.name:id), target: null });
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
alert("Failed to delete user");
|
||||
}
|
||||
}}
|
||||
onUpdateUserRole={async (id, edits) => {
|
||||
try {
|
||||
await api.updateUser(id, edits);
|
||||
await api.addAudit({ actor: meId, action: 'user_updated', summary: 'Updated ' + (userMap[id]?userMap[id].name:id) + ' permissions', target: null });
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
alert("Failed to update user");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -35,6 +35,52 @@ async def login_for_access_token(form_data: schemas.UserLogin, db: Session = Dep
|
||||
def read_users(db: Session = Depends(get_db)):
|
||||
return db.query(models.User).all()
|
||||
|
||||
@app.post("/users", response_model=schemas.User)
|
||||
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
|
||||
db_user = models.User(
|
||||
id=user.id,
|
||||
name=user.name,
|
||||
role=user.role,
|
||||
hue=user.hue,
|
||||
initials=user.initials,
|
||||
email=user.email,
|
||||
phone=user.phone,
|
||||
photo=user.photo,
|
||||
account_type=user.account_type,
|
||||
password_hash=auth.get_password_hash(user.password)
|
||||
)
|
||||
db.add(db_user)
|
||||
db.commit()
|
||||
db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
@app.patch("/users/{user_id}", response_model=schemas.User)
|
||||
def update_user(user_id: str, user_update: schemas.UserUpdate, db: Session = Depends(get_db)):
|
||||
db_user = db.query(models.User).filter(models.User.id == user_id).first()
|
||||
if not db_user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
update_data = user_update.dict(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(db_user, key, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
@app.delete("/users/{user_id}")
|
||||
def delete_user(user_id: str, db: Session = Depends(get_db)):
|
||||
db_user = db.query(models.User).filter(models.User.id == user_id).first()
|
||||
if not db_user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
# Reassign tasks to rod
|
||||
db.query(models.Task).filter(models.Task.assignee_id == user_id).update({"assignee_id": "rod"})
|
||||
|
||||
db.delete(db_user)
|
||||
db.commit()
|
||||
return {"message": "User deleted"}
|
||||
|
||||
@app.get("/tasks", response_model=List[schemas.Task])
|
||||
def read_tasks(db: Session = Depends(get_db)):
|
||||
return db.query(models.Task).all()
|
||||
|
||||
@@ -20,6 +20,12 @@ class UserLogin(BaseModel):
|
||||
id: str
|
||||
password: str
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
account_type: Optional[str] = None
|
||||
photo: Optional[str] = None
|
||||
|
||||
class User(UserBase):
|
||||
created_at: datetime
|
||||
class Config:
|
||||
|
||||
+3
-3
@@ -626,7 +626,7 @@ function FilterChip({ on, onClick, children }) {
|
||||
);
|
||||
}
|
||||
|
||||
function SettingsScreen({ user, isAdmin, onClose, onSave, onLogout, onSwitchUser, onCreateUser, onDeleteUser, onUpdateUserRole }) {
|
||||
function SettingsScreen({ user, dbUsers, isAdmin, onClose, onSave, onLogout, onSwitchUser, onCreateUser, onDeleteUser, onUpdateUserRole }) {
|
||||
const [name, setName] = React.useState(user.name);
|
||||
const [role, setRole] = React.useState(user.role);
|
||||
const [photo, setPhoto] = React.useState(user.photo || null);
|
||||
@@ -804,7 +804,7 @@ function SettingsScreen({ user, isAdmin, onClose, onSave, onLogout, onSwitchUser
|
||||
|
||||
{tab === 'workspace' && (
|
||||
<WorkspaceTab
|
||||
user={user} isAdmin={isAdmin}
|
||||
user={user} isAdmin={isAdmin} dbUsers={dbUsers}
|
||||
onSwitchUser={onSwitchUser}
|
||||
onCreateUser={onCreateUser}
|
||||
onDeleteUser={onDeleteUser}
|
||||
@@ -829,7 +829,7 @@ function ToggleRow({ label, defaultOn = false }) {
|
||||
);
|
||||
}
|
||||
|
||||
function WorkspaceTab({ user, isAdmin, dbUsers, onSwitchUser, onCreateUser, onDeleteUser, onUpdateUserRole }) {
|
||||
function WorkspaceTab({ user, isAdmin, dbUsers = [], onSwitchUser, onCreateUser, onDeleteUser, onUpdateUserRole }) {
|
||||
const [adding, setAdding] = React.useState(false);
|
||||
const [newName, setNewName] = React.useState('');
|
||||
const [newRole, setNewRole] = React.useState('');
|
||||
|
||||
Reference in New Issue
Block a user