Task #511: Fully remove Chat feature
Destructive removal per user confirmation ("حذف نهائي ما يرجع").
Removed:
- API: routes/conversations.ts, schema/conversations.ts, all chat
socket handlers in src/index.ts, /admin/users/:id/dependents/
conversations+messages endpoints, conversation/message dependency
counts in users/stats routes.
- Web: pages/chat.tsx, /chat route, dock chat filter, MessageSquare
icon and messages StatCard on home, all chat-related UI in
notifications + admin (dependency badges, delete-dialog rows,
UserDependentConversations/Messages sections, count map keys).
- Locales: nav.chat, home.stats.messages, full chat.* block,
admin.deleteUser conv/msgCount, admin.users.counts.conv/msg,
admin.audit.unit.conversation_*/message_*, admin.dependents.user*.
- OpenAPI spec: tags, all /conversations/* paths, conv/msg dependent
paths, related schemas (ConversationWithDetails, MessageWithSender,
UserDependentConversation/MessageItem+Page, etc.), UserProfile and
UserDeletionConflict conv/msg fields, HomeStats.unreadMessages.
Regenerated client via orval.
- Database: dropped message_reads, messages,
conversation_participants, conversations (CASCADE); deleted
notifications with related_type='conversation' or type='chat';
deleted apps row with slug='chat'; ran drizzle push-force.
- Seed: removed chat:access permission + user-role assignment +
seeded chat app entry from scripts/src/seed.ts.
- Tests: deleted conversations-leave.test.mjs; cleaned chat refs from
list-dependency-counts, delete-force-warnings, audit-log-coverage,
and admin-inline-dependency-counts (e2e) — replaced chat dependents
with note dependents where needed for force-delete coverage.
Notes preserved: notes.tsx noConversationsYet/conversationWith refer
to NOTE THREADS (not chat) and were intentionally NOT touched.
executive-meetings.ts not modified per replit.md restriction.
Pre-existing flaky test failures in executive-meetings/group/etc
suites remain unrelated to this task.
This commit is contained in:
@@ -18,10 +18,6 @@ tags:
|
||||
description: Internal services (khidmati)
|
||||
- name: orders
|
||||
description: Service orders workflow
|
||||
- name: conversations
|
||||
description: Chat conversations
|
||||
- name: messages
|
||||
description: Chat messages
|
||||
- name: notifications
|
||||
description: Notifications
|
||||
- name: users
|
||||
@@ -928,254 +924,6 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/ServiceCategory"
|
||||
|
||||
# Conversations
|
||||
/conversations:
|
||||
get:
|
||||
operationId: listConversations
|
||||
tags: [conversations]
|
||||
summary: List conversations for current user
|
||||
responses:
|
||||
"200":
|
||||
description: Conversations
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
post:
|
||||
operationId: createConversation
|
||||
tags: [conversations]
|
||||
summary: Create a conversation
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateConversationBody"
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
/conversations/{id}:
|
||||
get:
|
||||
operationId: getConversation
|
||||
tags: [conversations]
|
||||
summary: Get conversation by ID
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: Conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
patch:
|
||||
operationId: updateConversation
|
||||
tags: [conversations]
|
||||
summary: Update conversation properties (admin only)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UpdateConversationBody"
|
||||
responses:
|
||||
"200":
|
||||
description: Updated conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
/conversations/{id}/participants:
|
||||
post:
|
||||
operationId: addConversationParticipants
|
||||
tags: [conversations]
|
||||
summary: Add participants to a group conversation (admin only)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AddParticipantsBody"
|
||||
responses:
|
||||
"200":
|
||||
description: Updated conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
/conversations/{id}/participants/{userId}:
|
||||
delete:
|
||||
operationId: removeConversationParticipant
|
||||
tags: [conversations]
|
||||
summary: Remove a participant from a group conversation (admin only)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
- name: userId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: Updated conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
/conversations/{id}/state:
|
||||
patch:
|
||||
operationId: updateConversationState
|
||||
tags: [conversations]
|
||||
summary: Update per-user state (mute, archive)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UpdateConversationStateBody"
|
||||
responses:
|
||||
"200":
|
||||
description: Updated conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ConversationWithDetails"
|
||||
|
||||
/conversations/{id}/leave:
|
||||
post:
|
||||
operationId: leaveConversation
|
||||
tags: [conversations]
|
||||
summary: Leave a group conversation
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
successorId:
|
||||
type: integer
|
||||
description: |
|
||||
When the leaver is the only admin, optionally name a specific
|
||||
remaining member to promote. Omit (or send null) to keep the
|
||||
automatic behavior of promoting the longest-tenured member.
|
||||
nullable: true
|
||||
responses:
|
||||
"200":
|
||||
description: Left conversation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SuccessResponse"
|
||||
|
||||
/conversations/{id}/messages:
|
||||
get:
|
||||
operationId: listMessages
|
||||
tags: [messages]
|
||||
summary: List messages in a conversation
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: Messages
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/MessageWithSender"
|
||||
|
||||
post:
|
||||
operationId: sendMessage
|
||||
tags: [messages]
|
||||
summary: Send a message
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SendMessageBody"
|
||||
responses:
|
||||
"201":
|
||||
description: Message sent
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/MessageWithSender"
|
||||
|
||||
/conversations/{id}/read:
|
||||
post:
|
||||
operationId: markConversationRead
|
||||
tags: [messages]
|
||||
summary: Mark all messages in conversation as read
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: Marked as read
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SuccessResponse"
|
||||
|
||||
# Notifications
|
||||
/notifications:
|
||||
get:
|
||||
@@ -3074,70 +2822,6 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/admin/users/{id}/dependents/conversations:
|
||||
get:
|
||||
operationId: getAdminUserDependentConversations
|
||||
tags: [users]
|
||||
summary: List conversations created by a user
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
- in: query
|
||||
name: limit
|
||||
required: false
|
||||
schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
|
||||
- in: query
|
||||
name: offset
|
||||
required: false
|
||||
schema: { type: integer, minimum: 0, default: 0 }
|
||||
responses:
|
||||
"200":
|
||||
description: Paged list of conversations
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserDependentConversationsPage"
|
||||
"404":
|
||||
description: User not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/admin/users/{id}/dependents/messages:
|
||||
get:
|
||||
operationId: getAdminUserDependentMessages
|
||||
tags: [users]
|
||||
summary: List messages sent by a user
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
- in: query
|
||||
name: limit
|
||||
required: false
|
||||
schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
|
||||
- in: query
|
||||
name: offset
|
||||
required: false
|
||||
schema: { type: integer, minimum: 0, default: 0 }
|
||||
responses:
|
||||
"200":
|
||||
description: Paged list of messages
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserDependentMessagesPage"
|
||||
"404":
|
||||
description: User not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
components:
|
||||
schemas:
|
||||
HealthStatus:
|
||||
@@ -3463,16 +3147,6 @@ components:
|
||||
description: |
|
||||
Number of service orders owned by this user. Populated only by the
|
||||
admin list endpoint (GET /users).
|
||||
conversationCount:
|
||||
type: integer
|
||||
description: |
|
||||
Number of conversations created by this user. Populated only by the
|
||||
admin list endpoint (GET /users).
|
||||
messageCount:
|
||||
type: integer
|
||||
description: |
|
||||
Number of messages authored by this user. Populated only by the
|
||||
admin list endpoint (GET /users).
|
||||
required:
|
||||
- id
|
||||
- username
|
||||
@@ -3817,16 +3491,10 @@ components:
|
||||
type: integer
|
||||
orderCount:
|
||||
type: integer
|
||||
conversationCount:
|
||||
type: integer
|
||||
messageCount:
|
||||
type: integer
|
||||
required:
|
||||
- error
|
||||
- noteCount
|
||||
- orderCount
|
||||
- conversationCount
|
||||
- messageCount
|
||||
|
||||
AppDeletionConflict:
|
||||
type: object
|
||||
@@ -4304,161 +3972,6 @@ components:
|
||||
- deletedIds
|
||||
- failedIds
|
||||
|
||||
ConversationWithDetails:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
nameAr:
|
||||
type: ["string", "null"]
|
||||
nameEn:
|
||||
type: ["string", "null"]
|
||||
avatarUrl:
|
||||
type: ["string", "null"]
|
||||
isGroup:
|
||||
type: boolean
|
||||
createdBy:
|
||||
type: integer
|
||||
participants:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ParticipantInfo"
|
||||
lastMessage:
|
||||
$ref: "#/components/schemas/MessageWithSender"
|
||||
nullable: true
|
||||
unreadCount:
|
||||
type: integer
|
||||
isMuted:
|
||||
type: boolean
|
||||
isArchived:
|
||||
type: boolean
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- id
|
||||
- isGroup
|
||||
- createdBy
|
||||
- participants
|
||||
- unreadCount
|
||||
- isMuted
|
||||
- isArchived
|
||||
- createdAt
|
||||
- updatedAt
|
||||
|
||||
ParticipantInfo:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
username:
|
||||
type: string
|
||||
displayNameAr:
|
||||
type: ["string", "null"]
|
||||
displayNameEn:
|
||||
type: ["string", "null"]
|
||||
avatarUrl:
|
||||
type: ["string", "null"]
|
||||
isAdmin:
|
||||
type: boolean
|
||||
required:
|
||||
- id
|
||||
- username
|
||||
- isAdmin
|
||||
|
||||
CreateConversationBody:
|
||||
type: object
|
||||
properties:
|
||||
participantIds:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
nameAr:
|
||||
type: ["string", "null"]
|
||||
nameEn:
|
||||
type: ["string", "null"]
|
||||
avatarUrl:
|
||||
type: ["string", "null"]
|
||||
isGroup:
|
||||
type: boolean
|
||||
required:
|
||||
- participantIds
|
||||
|
||||
UpdateConversationBody:
|
||||
type: object
|
||||
properties:
|
||||
avatarUrl:
|
||||
type: ["string", "null"]
|
||||
nameAr:
|
||||
type: ["string", "null"]
|
||||
nameEn:
|
||||
type: ["string", "null"]
|
||||
|
||||
UpdateConversationStateBody:
|
||||
type: object
|
||||
properties:
|
||||
isMuted:
|
||||
type: boolean
|
||||
isArchived:
|
||||
type: boolean
|
||||
|
||||
AddParticipantsBody:
|
||||
type: object
|
||||
properties:
|
||||
userIds:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
required:
|
||||
- userIds
|
||||
|
||||
MessageWithSender:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
conversationId:
|
||||
type: integer
|
||||
senderId:
|
||||
type: integer
|
||||
content:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
enum: [user, group_renamed, members_added, member_removed, member_left, admin_promoted]
|
||||
meta:
|
||||
type: object
|
||||
nullable: true
|
||||
additionalProperties: true
|
||||
sender:
|
||||
$ref: "#/components/schemas/ParticipantInfo"
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- id
|
||||
- conversationId
|
||||
- senderId
|
||||
- content
|
||||
- kind
|
||||
- sender
|
||||
- createdAt
|
||||
- updatedAt
|
||||
|
||||
SendMessageBody:
|
||||
type: object
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
|
||||
Notification:
|
||||
type: object
|
||||
properties:
|
||||
@@ -4579,15 +4092,12 @@ components:
|
||||
type: integer
|
||||
unreadNotifications:
|
||||
type: integer
|
||||
unreadMessages:
|
||||
type: integer
|
||||
totalUsers:
|
||||
type: integer
|
||||
required:
|
||||
- totalApps
|
||||
- totalServices
|
||||
- unreadNotifications
|
||||
- unreadMessages
|
||||
- totalUsers
|
||||
|
||||
AdminStats:
|
||||
@@ -5179,55 +4689,6 @@ components:
|
||||
nextOffset: { type: ["integer", "null"] }
|
||||
required: [items, totalCount, limit, offset, nextOffset]
|
||||
|
||||
UserDependentConversationItem:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: integer }
|
||||
nameAr: { type: ["string", "null"] }
|
||||
nameEn: { type: ["string", "null"] }
|
||||
isGroup: { type: boolean }
|
||||
createdAt: { type: string, format: date-time }
|
||||
required: [id, nameAr, nameEn, isGroup, createdAt]
|
||||
|
||||
UserDependentConversationsPage:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/UserDependentConversationItem"
|
||||
totalCount: { type: integer }
|
||||
limit: { type: integer }
|
||||
offset: { type: integer }
|
||||
nextOffset: { type: ["integer", "null"] }
|
||||
required: [items, totalCount, limit, offset, nextOffset]
|
||||
|
||||
UserDependentMessageItem:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: integer }
|
||||
content: { type: string }
|
||||
kind: { type: string }
|
||||
createdAt: { type: string, format: date-time }
|
||||
conversationId: { type: integer }
|
||||
conversationNameAr: { type: ["string", "null"] }
|
||||
conversationNameEn: { type: ["string", "null"] }
|
||||
conversationIsGroup: { type: boolean }
|
||||
required: [id, content, kind, createdAt, conversationId, conversationNameAr, conversationNameEn, conversationIsGroup]
|
||||
|
||||
UserDependentMessagesPage:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/UserDependentMessageItem"
|
||||
totalCount: { type: integer }
|
||||
limit: { type: integer }
|
||||
offset: { type: integer }
|
||||
nextOffset: { type: ["integer", "null"] }
|
||||
required: [items, totalCount, limit, offset, nextOffset]
|
||||
|
||||
TopUserItem:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in New Issue
Block a user