Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Public-vehicle badge.** Vehicles marked *Show on profile* now carry a small
**Public** badge in the garage's Vehicles tab, so you can see at a glance which are
shared on your driver profile.
- **Request a datalogger.** The contact form has a new **New datalogger connection**
category, and the "Download from logger" picker has a **Request additional
dataloggers** button that opens the contact form with that category preselected.
- **Custom tracks ride along with leaderboard snapshots.** When you submit a snapshot
for a track that isn't in the built-in list, its layout + sectors are included with
the snapshot (so others can view the lap) and the track is **automatically submitted
Expand Down
39 changes: 32 additions & 7 deletions src/components/ContactDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { Mail } from "lucide-react";
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
Expand All @@ -9,8 +9,14 @@ import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { toast } from "@/hooks/use-toast";

// The "request a new datalogger" category — exported so the logger picker can
// open this dialog with it preselected. Keep the VALUE in sync with the edge
// function's ALLOWED_CATEGORIES + the admin MessagesTab category map.
// eslint-disable-next-line react-refresh/only-export-components -- co-located with the dialog that owns the categories
export const MESSAGE_CATEGORIES = ["Comment", "Feature Request", "Complaint", "Bug Report"] as const;
export const CATEGORY_NEW_DATALOGGER = "New Datalogger Connection";

// eslint-disable-next-line react-refresh/only-export-components -- co-located with the dialog that owns the categories
export const MESSAGE_CATEGORIES = ["Comment", "Feature Request", "Complaint", "Bug Report", CATEGORY_NEW_DATALOGGER] as const;

// The category VALUE submitted to the backend stays the English string above;
// this only maps it to a locale key for display.
Expand All @@ -19,16 +25,33 @@ const CATEGORY_KEYS = {
"Feature Request": "featureRequest",
"Complaint": "complaint",
"Bug Report": "bugReport",
[CATEGORY_NEW_DATALOGGER]: "newDatalogger",
} as const;

export function ContactDialog({ variant = "footer" }: { variant?: "header" | "footer" }) {
export function ContactDialog({
variant = "footer",
trigger,
defaultCategory,
}: {
variant?: "header" | "footer";
/** Custom trigger element (overrides the default header/footer button). */
trigger?: ReactNode;
/** Preselect a category when the dialog opens (e.g. from the logger picker). */
defaultCategory?: string;
}) {
const { t } = useTranslation("landing");
const [open, setOpen] = useState(false);
const [category, setCategory] = useState<string>("");
const [category, setCategory] = useState<string>(defaultCategory ?? "");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const [submitting, setSubmitting] = useState(false);

// Re-seed the preselected category each time the dialog opens.
const handleOpenChange = (next: boolean) => {
setOpen(next);
if (next && defaultCategory) setCategory(defaultCategory);
};

const handleSubmit = async () => {
if (!category || !message.trim()) {
toast({ title: t("contact.missingFields"), description: t("contact.missingFieldsDesc"), variant: "destructive" });
Expand All @@ -55,7 +78,7 @@ export function ContactDialog({ variant = "footer" }: { variant?: "header" | "fo
}

toast({ title: t("contact.sent"), description: t("contact.sentDesc") });
setCategory("");
setCategory(defaultCategory ?? "");
setEmail("");
setMessage("");
setOpen(false);
Expand All @@ -67,9 +90,11 @@ export function ContactDialog({ variant = "footer" }: { variant?: "header" | "fo
};

return (
<Dialog open={open} onOpenChange={setOpen}>
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
{variant === "header" ? (
{trigger ? (
trigger
) : variant === "header" ? (
<Button variant="default" size="sm" className="gap-2">
<Mail className="w-4 h-4" />
<span className="hidden sm:inline">{t("contact.trigger")}</span>
Expand Down
13 changes: 13 additions & 0 deletions src/components/LoggerPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { MailPlus } from "lucide-react";
import { Button } from "@/components/ui/button";
import { ContactDialog, CATEGORY_NEW_DATALOGGER } from "@/components/ContactDialog";
import { isNativeApp } from "@/lib/platform";
import { cn } from "@/lib/utils";

Expand Down Expand Up @@ -140,6 +142,17 @@ export function LoggerPicker({ open, onOpenChange, bleSupported, onSelectFledgli
/>
</div>

{/* Don't see your logger? Open the contact form pre-set to request one. */}
<ContactDialog
defaultCategory={CATEGORY_NEW_DATALOGGER}
trigger={
<Button variant="outline" className="w-full gap-2">
<MailPlus className="h-4 w-4" />
{t("requestLogger")}
</Button>
}
/>

<p className="text-[11px] leading-relaxed text-muted-foreground/70">
{t("trademarks")}
</p>
Expand Down
2 changes: 2 additions & 0 deletions src/components/admin/MessagesTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const categoryColors: Record<string, string> = {
"Feature Request": "bg-green-500/20 text-green-400 border-green-500/30",
"Complaint": "bg-red-500/20 text-red-400 border-red-500/30",
"Bug Report": "bg-orange-500/20 text-orange-400 border-orange-500/30",
"New Datalogger Connection": "bg-purple-500/20 text-purple-400 border-purple-500/30",
};

// Map the English category value stored in the DB to its locale key.
Expand All @@ -32,6 +33,7 @@ const categoryKeys = {
"Feature Request": "featureRequest",
"Complaint": "complaint",
"Bug Report": "bugReport",
"New Datalogger Connection": "newDatalogger",
} as const;

export function MessagesTab({ onUnreadCount }: { onUnreadCount?: (count: number) => void }) {
Expand Down
3 changes: 2 additions & 1 deletion src/locales/de/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"comment": "Kommentar",
"featureRequest": "Funktionswunsch",
"complaint": "Beschwerde",
"bugReport": "Fehlerbericht"
"bugReport": "Fehlerbericht",
"newDatalogger": "New datalogger connection"
},
"emailLine": "E-Mail: {{email}}",
"ipLine": "IP: {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/de/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"comment": "Kommentar",
"featureRequest": "Funktionswunsch",
"complaint": "Beschwerde",
"bugReport": "Fehlerbericht"
"bugReport": "Fehlerbericht",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "E-Mail",
"emailOptional": "(optional, für eine Antwort)",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/de/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@
"retry": "Erneut versuchen",
"cancel": "Abbrechen"
}
}
},
"requestLogger": "Request additional dataloggers"
}
3 changes: 2 additions & 1 deletion src/locales/en/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@
"comment": "Comment",
"featureRequest": "Feature Request",
"complaint": "Complaint",
"bugReport": "Bug Report"
"bugReport": "Bug Report",
"newDatalogger": "New datalogger connection"
},
"emailLine": "Email: {{email}}",
"ipLine": "IP: {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/en/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"comment": "Comment",
"featureRequest": "Feature Request",
"complaint": "Complaint",
"bugReport": "Bug Report"
"bugReport": "Bug Report",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "Email",
"emailOptional": "(optional, for a reply)",
Expand Down
1 change: 1 addition & 0 deletions src/locales/en/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"subtitle": "Choose your logger to start a download.",
"comingSoon": "Coming soon",
"close": "Close",
"requestLogger": "Request additional dataloggers",
"trademarks": "MyChron is a trademark of AiM Tech S.r.l. and Alfano is a trademark of Alfano S.A. LapWing is independent and not affiliated with or endorsed by them; all product names, logos, and brands are the property of their respective owners.",
"tags": {
"fledgling": "DIY logger · Bluetooth LE",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/es/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"comment": "Comentario",
"featureRequest": "Solicitud de función",
"complaint": "Queja",
"bugReport": "Informe de error"
"bugReport": "Informe de error",
"newDatalogger": "New datalogger connection"
},
"emailLine": "Correo: {{email}}",
"ipLine": "IP: {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/es/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"comment": "Comentario",
"featureRequest": "Solicitud de función",
"complaint": "Queja",
"bugReport": "Informe de error"
"bugReport": "Informe de error",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "Correo electrónico",
"emailOptional": "(opcional, para responderte)",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/es/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@
"retry": "Reintentar",
"cancel": "Cancelar"
}
}
},
"requestLogger": "Request additional dataloggers"
}
3 changes: 2 additions & 1 deletion src/locales/fr/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"comment": "Commentaire",
"featureRequest": "Demande de fonctionnalité",
"complaint": "Réclamation",
"bugReport": "Rapport de bug"
"bugReport": "Rapport de bug",
"newDatalogger": "New datalogger connection"
},
"emailLine": "E-mail : {{email}}",
"ipLine": "IP : {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/fr/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"comment": "Commentaire",
"featureRequest": "Demande de fonctionnalité",
"complaint": "Réclamation",
"bugReport": "Rapport de bug"
"bugReport": "Rapport de bug",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "E-mail",
"emailOptional": "(facultatif, pour une réponse)",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/fr/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@
"retry": "Réessayer",
"cancel": "Annuler"
}
}
},
"requestLogger": "Request additional dataloggers"
}
3 changes: 2 additions & 1 deletion src/locales/it/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"comment": "Commento",
"featureRequest": "Richiesta di funzionalità",
"complaint": "Reclamo",
"bugReport": "Segnalazione di bug"
"bugReport": "Segnalazione di bug",
"newDatalogger": "New datalogger connection"
},
"emailLine": "Email: {{email}}",
"ipLine": "IP: {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/it/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"comment": "Commento",
"featureRequest": "Richiesta di funzionalità",
"complaint": "Reclamo",
"bugReport": "Segnalazione di bug"
"bugReport": "Segnalazione di bug",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "Email",
"emailOptional": "(facoltativa, per una risposta)",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/it/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@
"retry": "Riprova",
"cancel": "Annulla"
}
}
},
"requestLogger": "Request additional dataloggers"
}
3 changes: 2 additions & 1 deletion src/locales/ja/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"comment": "コメント",
"featureRequest": "機能リクエスト",
"complaint": "苦情",
"bugReport": "バグ報告"
"bugReport": "バグ報告",
"newDatalogger": "New datalogger connection"
},
"emailLine": "メール: {{email}}",
"ipLine": "IP: {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/ja/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"comment": "コメント",
"featureRequest": "機能リクエスト",
"complaint": "苦情",
"bugReport": "バグ報告"
"bugReport": "バグ報告",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "メールアドレス",
"emailOptional": "(任意、返信用)",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/ja/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@
"retry": "再試行",
"cancel": "キャンセル"
}
}
},
"requestLogger": "Request additional dataloggers"
}
3 changes: 2 additions & 1 deletion src/locales/pt-BR/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"comment": "Comentário",
"featureRequest": "Solicitação de recurso",
"complaint": "Reclamação",
"bugReport": "Relatório de bug"
"bugReport": "Relatório de bug",
"newDatalogger": "New datalogger connection"
},
"emailLine": "E-mail: {{email}}",
"ipLine": "IP: {{ip}}",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/pt-BR/landing.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@
"comment": "Comentário",
"featureRequest": "Solicitação de recurso",
"complaint": "Reclamação",
"bugReport": "Relatório de bug"
"bugReport": "Relatório de bug",
"newDatalogger": "New datalogger connection"
},
"emailLabel": "E-mail",
"emailOptional": "(opcional, para resposta)",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/pt-BR/logger.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@
"retry": "Tentar novamente",
"cancel": "Cancelar"
}
}
},
"requestLogger": "Request additional dataloggers"
}
2 changes: 1 addition & 1 deletion supabase/functions/submit-message/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const corsHeaders = {
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version',
};

const ALLOWED_CATEGORIES = ["Comment", "Feature Request", "Complaint", "Bug Report"];
const ALLOWED_CATEGORIES = ["Comment", "Feature Request", "Complaint", "Bug Report", "New Datalogger Connection"];

Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
Expand Down
Loading