Merge branch 'main' into feat/planner-colormode-select

This commit is contained in:
Ullrich Schäfer 2026-07-16 01:03:09 +02:00 committed by GitHub
commit 24913b9f3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 35 additions and 32 deletions

View file

@ -52,45 +52,45 @@ export function ExportButton({ yjs }: { yjs: YjsState }) {
const hasMultipleDays = hasDayBreaks(yjs);
const item =
"block w-full px-3 py-1.5 text-left transition-colors hover:bg-bg-subtle";
const split =
"h-7 border border-border bg-bg-raised text-sm font-medium text-text-hi transition-colors hover:bg-bg-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent";
return (
<div ref={ref} className="relative z-[1001]">
<div className="flex">
<button
onClick={handleExportRoute}
className="rounded-l bg-gray-100 px-3 py-1 text-sm text-gray-700 hover:bg-gray-200"
className={`${split} rounded-l-md border-r-0 px-3`}
>
{t("exportGpx")}
</button>
<button
onClick={(e) => { e.stopPropagation(); setOpen((v) => !v); }}
className="rounded-r border-l border-gray-300 bg-gray-100 px-2.5 py-1 text-sm text-gray-700 hover:bg-gray-200"
aria-label={t("exportOptions")}
aria-expanded={open}
className={`${split} rounded-r-md px-2 text-text-md`}
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
</div>
{open && (
<div className="absolute left-0 top-full z-50 mt-1 w-56 rounded border border-gray-200 bg-white py-1 shadow-lg">
<button
onClick={handleExportRoute}
className="block w-full px-3 py-1.5 text-left hover:bg-gray-100"
>
<span className="text-sm text-gray-700">{t("exportRoute")}</span>
<span className="block text-xs text-gray-400">{t("exportRouteDesc")}</span>
<div className="absolute left-0 top-full z-50 mt-1 w-56 rounded-md border border-border bg-bg-raised py-1 shadow-md">
<button onClick={handleExportRoute} className={item}>
<span className="text-sm text-text-hi">{t("exportRoute")}</span>
<span className="block text-xs text-text-lo">{t("exportRouteDesc")}</span>
</button>
<button
onClick={handleExportPlan}
className="block w-full px-3 py-1.5 text-left hover:bg-gray-100"
>
<span className="text-sm text-gray-700">{t("exportPlan")}</span>
<span className="block text-xs text-gray-400">{t("exportPlanDesc")}</span>
<button onClick={handleExportPlan} className={item}>
<span className="text-sm text-text-hi">{t("exportPlan")}</span>
<span className="block text-xs text-text-lo">{t("exportPlanDesc")}</span>
</button>
{hasMultipleDays && (
<button
onClick={handleExportDays}
className="block w-full px-3 py-1.5 text-left hover:bg-gray-100"
>
<span className="text-sm text-gray-700">{t("exportDays")}</span>
<span className="block text-xs text-gray-400">{t("exportDaysDesc")}</span>
<button onClick={handleExportDays} className={item}>
<span className="text-sm text-text-hi">{t("exportDays")}</span>
<span className="block text-xs text-text-lo">{t("exportDaysDesc")}</span>
</button>
)}
</div>

View file

@ -1,5 +1,6 @@
import { useState, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "@trails-cool/ui";
import { computeSurfaceBreakdown } from "@trails-cool/map-core";
import type { YjsState } from "~/lib/use-yjs";
import { buildPlanGpx } from "~/lib/gpx-export";
@ -64,17 +65,16 @@ export function SaveToJournalButton({ yjs, sessionId, returnUrl }: SaveToJournal
return (
<div className="flex items-center gap-2">
<button
onClick={handleSave}
disabled={saving}
className="rounded bg-green-600 px-3 py-1 text-sm text-white hover:bg-green-700 disabled:opacity-50"
>
<Button variant="primary" size="sm" onClick={handleSave} disabled={saving}>
{saving ? t("saving") : t("saveToJournal")}
</button>
{saved && <span className="text-xs text-green-600">{t("saved")}</span>}
{error && <span className="text-xs text-red-600">{error}</span>}
</Button>
{saved && <span className="text-xs text-accent">{t("saved")}</span>}
{error && <span className="text-xs text-danger">{error}</span>}
{saved && returnUrl && (
<a href={returnUrl} className="rounded bg-gray-100 px-2 py-1 text-xs text-gray-700 hover:bg-gray-200">
<a
href={returnUrl}
className="inline-flex h-7 items-center rounded-md border border-border bg-bg-raised px-2.5 text-xs font-medium text-text-hi transition-colors hover:bg-bg-subtle"
>
{t("returnToJournal")}
</a>
)}

View file

@ -91,7 +91,7 @@ test.describe("Planner routing", () => {
const sidebar = page.locator("aside");
await expect(sidebar.getByText("Refill water here")).toBeVisible({ timeout: 5000 });
await page.getByRole("button", { name: "" }).click();
await page.getByRole("button", { name: "Export options" }).click();
await expect(page.getByText("Export Plan")).toBeVisible({ timeout: 3000 });
const downloadPromise = page.waitForEvent("download");
await page.getByText("Export Plan").first().click();

View file

@ -18,6 +18,7 @@ export default {
newSession: "Neue Sitzung",
saveRoute: "Route speichern",
exportGpx: "GPX exportieren",
exportOptions: "Export-Optionen",
exportRoute: "Route exportieren",
exportRouteDesc: "GPX-Track für jede App",
exportPlan: "Plan exportieren",

View file

@ -18,6 +18,7 @@ export default {
newSession: "New Session",
saveRoute: "Save Route",
exportGpx: "Export GPX",
exportOptions: "Export options",
exportRoute: "Export Route",
exportRouteDesc: "Clean GPX track for any app",
exportPlan: "Export Plan",

View file

@ -41,6 +41,7 @@
--color-stop-border: rgba(139, 109, 58, 0.25);
/* ── Danger / no-go ── */
--color-danger: #a03c3c; /* solid danger — error text, matches the no-go hue */
--color-nogo: rgba(160, 60, 60, 0.12);
--color-nogo-border: rgba(160, 60, 60, 0.3);