mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 23:46:35 +00:00
feat(tours): download a saved tour as a GPX file (PR1-06A)
A saved tour can be handed over as a GPX file on purpose: its whole saved line as one track with the heights it has and none it lacks, its name, description and link, and the activity in <type>. The file is named after the tour. A tour without a line of two points is refused with 422 rather than written as a file that imports as nothing; the export changes nothing. GET /api/trips/:tripId/tours/:placeId/export.gpx, readable by anyone who sees the trip, and the MCP tool export_tour_gpx with the same rules. The tour detail gets a "GPX" action beside the navigation, the planner head a download button that waits while the tour has unsaved changes. The tours rail head now uses the same plain icon buttons as the editor.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import apiClient from './client';
|
||||
|
||||
/**
|
||||
* The file name a download's Content-Disposition gives: the UTF-8 `filename*`
|
||||
* first, since it keeps the tour's own script, the plain `filename` after.
|
||||
*/
|
||||
export function attachmentFilename(header: unknown): string | null {
|
||||
if (typeof header !== 'string') return null;
|
||||
const encoded = /filename\*=UTF-8''([^;]+)/i.exec(header)?.[1];
|
||||
if (encoded) {
|
||||
try {
|
||||
return decodeURIComponent(encoded.trim());
|
||||
} catch {
|
||||
// A malformed escape falls through to the plain name.
|
||||
}
|
||||
}
|
||||
return /filename="([^"]+)"/i.exec(header)?.[1] ?? null;
|
||||
}
|
||||
|
||||
/** PR1-06A: one saved tour as the GPX file the server writes. */
|
||||
export const tourGpxExportApi = {
|
||||
download: async (
|
||||
tripId: number | string,
|
||||
placeId: number,
|
||||
signal?: AbortSignal
|
||||
): Promise<{ blob: Blob; filename: string | null }> => {
|
||||
const response = await apiClient.get<Blob>(`/trips/${tripId}/tours/${placeId}/export.gpx`, {
|
||||
responseType: 'blob',
|
||||
signal,
|
||||
});
|
||||
return { blob: response.data, filename: attachmentFilename(response.headers['content-disposition']) };
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user