Replace status_id with in_progress boolean field

- Remove task_statuses lookup table and StatusID foreign key
- Add InProgress boolean field to Task model
- Add database migration (005_replace_status_with_in_progress)
- Update all handlers, services, and repositories
- Update admin frontend to display in_progress as checkbox/boolean
- Remove Task Statuses tab from admin lookups page
- Update tests to use InProgress instead of StatusID
- Task categorization now uses InProgress for kanban column assignment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-08 20:48:16 -06:00
parent cb250f108b
commit c5b0225422
43 changed files with 353 additions and 753 deletions

View File

@@ -32,7 +32,8 @@ func TestIntegration_ContractorSharingFlow(t *testing.T) {
var residenceResp map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &residenceResp)
require.NoError(t, err)
residenceCID := residenceResp["id"].(float64)
residenceData := residenceResp["data"].(map[string]interface{})
residenceCID := residenceData["id"].(float64)
// ========== User A shares residence C with User B ==========
// Generate share code
@@ -191,7 +192,8 @@ func TestIntegration_ContractorAccessWithoutResidenceShare(t *testing.T) {
var residenceResp map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &residenceResp)
residenceID := residenceResp["id"].(float64)
residenceData := residenceResp["data"].(map[string]interface{})
residenceID := residenceData["id"].(float64)
// User A creates a contractor tied to the residence (NOT shared with User B)
contractorBody := map[string]interface{}{
@@ -235,9 +237,10 @@ func TestIntegration_ContractorUpdateAndDeleteAccess(t *testing.T) {
w := app.makeAuthenticatedRequest(t, "POST", "/api/residences", residenceBody, userAToken)
require.Equal(t, http.StatusCreated, w.Code)
var residenceResp map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &residenceResp)
residenceID := residenceResp["id"].(float64)
var residenceResp2 map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &residenceResp2)
residenceData2 := residenceResp2["data"].(map[string]interface{})
residenceID := residenceData2["id"].(float64)
// Share with User B
w = app.makeAuthenticatedRequest(t, "POST", "/api/residences/"+formatID(residenceID)+"/generate-share-code", nil, userAToken)
@@ -259,9 +262,9 @@ func TestIntegration_ContractorUpdateAndDeleteAccess(t *testing.T) {
w = app.makeAuthenticatedRequest(t, "POST", "/api/contractors", contractorBody, userAToken)
require.Equal(t, http.StatusCreated, w.Code)
var contractorResp map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &contractorResp)
contractorID := contractorResp["id"].(float64)
var contractorResp3 map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &contractorResp3)
contractorID3 := contractorResp3["id"].(float64)
// User B (with access) can update the contractor
// Note: Must include residence_id to keep it tied to the residence
@@ -269,7 +272,7 @@ func TestIntegration_ContractorUpdateAndDeleteAccess(t *testing.T) {
"name": "Updated by User B",
"residence_id": uint(residenceID),
}
w = app.makeAuthenticatedRequest(t, "PUT", "/api/contractors/"+formatID(contractorID), updateBody, userBToken)
w = app.makeAuthenticatedRequest(t, "PUT", "/api/contractors/"+formatID(contractorID3), updateBody, userBToken)
assert.Equal(t, http.StatusOK, w.Code, "User B should be able to update contractor in shared residence")
// User C (without access) cannot update the contractor
@@ -277,15 +280,15 @@ func TestIntegration_ContractorUpdateAndDeleteAccess(t *testing.T) {
"name": "Hacked by User C",
"residence_id": uint(residenceID),
}
w = app.makeAuthenticatedRequest(t, "PUT", "/api/contractors/"+formatID(contractorID), updateBody2, userCToken)
w = app.makeAuthenticatedRequest(t, "PUT", "/api/contractors/"+formatID(contractorID3), updateBody2, userCToken)
assert.Equal(t, http.StatusForbidden, w.Code, "User C should NOT be able to update contractor")
// User C cannot delete the contractor
w = app.makeAuthenticatedRequest(t, "DELETE", "/api/contractors/"+formatID(contractorID), nil, userCToken)
w = app.makeAuthenticatedRequest(t, "DELETE", "/api/contractors/"+formatID(contractorID3), nil, userCToken)
assert.Equal(t, http.StatusForbidden, w.Code, "User C should NOT be able to delete contractor")
// User B (with access) can delete the contractor
w = app.makeAuthenticatedRequest(t, "DELETE", "/api/contractors/"+formatID(contractorID), nil, userBToken)
w = app.makeAuthenticatedRequest(t, "DELETE", "/api/contractors/"+formatID(contractorID3), nil, userBToken)
assert.Equal(t, http.StatusOK, w.Code, "User B should be able to delete contractor in shared residence")
}