mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
feat(collections): list details, place detail sheet, add-place, fusion kick (#1081)
Dashboard widget (B): the collections tool now shows the user's LISTS as compact colour-washed badges (cover image tinted with the list colour, or a gradient) that jump to the list — one list() call, no N+1. List details (C): lists gain a description, a custom cover image (uploaded to /uploads/covers, tinted with the list colour in the hero) and links. A shared ListEditorModal handles both create and edit; the hero shows the description + link chips. New `links` JSON column on collections + collection_places (migration 151) with parse/serialize in the service; a POST :id/cover upload endpoint mirroring trips; cover-file cleanup path-confined locally. Place detail (D): clicking a place opens a bottom sheet (no backdrop, so the map stays visible) — status cycle, copy-to-trip, remove, and an edit mode with a markdown description + links editor (collectionsApi.updatePlace, now wired via a store action). A "+" next to the search adds a place to the list via the maps search. Fusion + fixes (E): the owner can now remove an accepted member (kick) — new removeMember service/route/store + a button in ShareCollectionModal, with a collections:removed WS bounce. findMembership no longer matches on name alone (coordinate proximity required, killing "Starbucks everywhere" false positives). loadCollection swallows a 403/404 after a leave/remove so the URL sync can't throw uncaught. Grid remnants gone; the map select toggle moved onto the map. New strings across all 22 locales; i18n parity strict passes.
This commit is contained in:
@@ -51,6 +51,8 @@ export const collectionsApi = {
|
||||
ax.post(base, body satisfies CollectionCreateRequest).then((r: AxiosResponse) => r.data),
|
||||
update: (id: number, body: CollectionUpdateRequest): Promise<{ collection: Collection }> =>
|
||||
ax.patch(`${base}/${id}`, body satisfies CollectionUpdateRequest).then((r: AxiosResponse) => r.data),
|
||||
uploadCover: (id: number, formData: FormData): Promise<Collection> =>
|
||||
ax.post(`${base}/${id}/cover`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then((r: AxiosResponse) => r.data),
|
||||
remove: (id: number): Promise<unknown> =>
|
||||
ax.delete(`${base}/${id}`).then((r: AxiosResponse) => r.data),
|
||||
reorder: (orderedIds: number[]): Promise<unknown> =>
|
||||
@@ -84,6 +86,8 @@ export const collectionsApi = {
|
||||
ax.post(`${base}/invite/cancel`, { collection_id: collectionId, user_id: userId } satisfies CollectionInviteCancelRequest).then((r: AxiosResponse) => r.data),
|
||||
leave: (collectionId: number): Promise<unknown> =>
|
||||
ax.post(`${base}/leave`, { collection_id: collectionId }).then((r: AxiosResponse) => r.data),
|
||||
removeMember: (collectionId: number, userId: number): Promise<unknown> =>
|
||||
ax.post(`${base}/members/remove`, { collection_id: collectionId, user_id: userId }).then((r: AxiosResponse) => r.data),
|
||||
availableUsers: (id: number): Promise<{ users: { id: number; username: string }[] }> =>
|
||||
ax.get(`${base}/${id}/available-users`).then((r: AxiosResponse) => r.data),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user