1 Commits

Author SHA1 Message Date
bernard-ng 530ff2d1e4 chore(release): v1.0.0 2025-10-12 13:47:44 +02:00
8 changed files with 29 additions and 47 deletions
+6
View File
@@ -36,5 +36,11 @@ jobs:
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Set up database
run: |
APP_ENV=test php bin/console doctrine:database:create --env=test
APP_ENV=test php bin/console doctrine:migrations:migrate --no-interaction --env=test
APP_ENV=test php bin/console doctrine:fixtures:load --no-interaction --env=test
- name: Run PHPUnit
run: vendor/bin/phpunit
-19
View File
@@ -7,7 +7,6 @@ import { ConfirmSignalDialog } from "@/components/app/confirm-signal-dialog";
import { HeaderOverlay } from "@/components/app/header-overlay";
import { SidebarPanels } from "@/components/app/sidebar-panels";
import { MapViewport } from "@/components/map/map-viewport";
// Alert dialog moved into a dedicated component
import { SidebarProvider, Sidebar, SidebarContent, SidebarInset } from "@/components/ui/sidebar";
import { Toaster } from "@/components/ui/sonner";
import { useFeedDerivations } from "@/hooks/use-feed-derivations";
@@ -41,12 +40,7 @@ export default function App() {
const isRequestingLocation = useAppStore(state => state.isRequestingLocation);
const isDetailsOpen = useAppStore(state => state.isSidebarOpen);
const setIsDetailsOpen = useAppStore(state => state.setIsSidebarOpen);
const isHeaderCollapsed = useAppStore(state => state.isHeaderCollapsed);
const setIsHeaderCollapsed = useAppStore(state => state.setIsHeaderCollapsed);
const isMobileHeaderOpen = useAppStore(state => state.isMobileHeaderOpen);
const setIsMobileHeaderOpen = useAppStore(state => state.setIsMobileHeaderOpen);
const tileProvider = useAppStore(state => state.tileProvider);
const setTileProvider = useAppStore(state => state.setTileProvider);
const { refresh: refreshLocation } = useUserLocation();
const {
@@ -73,8 +67,6 @@ export default function App() {
[selectVisibleLatestByUser, userLocation]
);
// Derived lists depend on focusOn; initialize map first, then derive.
const statusLabel = t(`status.${status}`);
const lastUpdatedLabel = lastUpdated ? formatRelativeTime(lastUpdated, locale) : t("common.never");
const isLoading = status === "loading";
@@ -166,11 +158,9 @@ export default function App() {
setIsConfirmOpen(true);
}, [userLocation]);
// recentActivity and dangerCells returned above
const confirmationLat = pendingSpot ? formatCoordinate(pendingSpot.lat, locale) : "--";
const confirmationLng = pendingSpot ? formatCoordinate(pendingSpot.lng, locale) : "--";
const isDialogDisabled = !pendingSpot || isConfirming;
const detailsToggleLabel = isDetailsOpen ? t("details.close") : t("details.open");
return (
<>
@@ -187,9 +177,6 @@ export default function App() {
isBusy={isPosting || isConfirming}
locationHint={locationHint}
showLocationCta={showLocationCta}
hasLocation={hasLocation}
tileProvider={tileProvider}
setTileProvider={setTileProvider}
dangerCells={dangerCells}
recentActivity={recentActivity}
/>
@@ -219,12 +206,6 @@ export default function App() {
disableHeat={visibleDensity.length === 0}
disableLocate={!hasLocation}
disableMySignal={!myVisibleSignal}
isDetailsOpen={isDetailsOpen}
detailsToggleLabel={detailsToggleLabel}
isHeaderCollapsed={isHeaderCollapsed}
setIsHeaderCollapsed={setIsHeaderCollapsed}
isMobileHeaderOpen={isMobileHeaderOpen}
setIsMobileHeaderOpen={setIsMobileHeaderOpen}
/>
<ConfirmSignalDialog
+7 -12
View File
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { AppHeader } from "@/components/layout/app-header";
import { Badge } from "@/components/ui/badge";
import { SidebarTrigger } from "@/components/ui/sidebar";
import { useAppStore } from "@/store/use-app-store";
import type { FeedStatus } from "@/types/api";
interface HeaderOverlayProps {
@@ -18,12 +19,6 @@ interface HeaderOverlayProps {
disableHeat: boolean;
disableLocate: boolean;
disableMySignal: boolean;
isDetailsOpen: boolean;
detailsToggleLabel: string;
isHeaderCollapsed: boolean;
setIsHeaderCollapsed: (collapsed: boolean) => void;
isMobileHeaderOpen: boolean;
setIsMobileHeaderOpen: (open: boolean) => void;
}
export function HeaderOverlay({
@@ -38,14 +33,14 @@ export function HeaderOverlay({
disableHeat,
disableLocate,
disableMySignal,
isDetailsOpen,
detailsToggleLabel,
isHeaderCollapsed,
setIsHeaderCollapsed,
isMobileHeaderOpen,
setIsMobileHeaderOpen,
}: HeaderOverlayProps) {
const { t } = useTranslation();
const isDetailsOpen = useAppStore(state => state.isSidebarOpen);
const isHeaderCollapsed = useAppStore(state => state.isHeaderCollapsed);
const setIsHeaderCollapsed = useAppStore(state => state.setIsHeaderCollapsed);
const isMobileHeaderOpen = useAppStore(state => state.isMobileHeaderOpen);
const setIsMobileHeaderOpen = useAppStore(state => state.setIsMobileHeaderOpen);
const detailsToggleLabel = isDetailsOpen ? t("details.close") : t("details.open");
return (
<div className="pointer-events-none absolute inset-0 flex flex-col">
+3 -8
View File
@@ -6,8 +6,8 @@ import { MapTilesPanel } from "@/components/panels/map-tiles-panel";
import { OverviewPanel } from "@/components/panels/overview-panel";
import { ScrollArea } from "@/components/ui/scroll-area";
import type { FeedError } from "@/hooks/use-hotsop-feed";
import type { TileProvider } from "@/hooks/use-leaflet-heatmap";
import { formatRelativeTime } from "@/lib/utils";
import { useAppStore } from "@/store/use-app-store";
type DangerCell = {
id: string;
@@ -36,9 +36,6 @@ interface SidebarPanelsProps {
isBusy: boolean;
locationHint: string;
showLocationCta: boolean;
hasLocation: boolean;
tileProvider: TileProvider;
setTileProvider: (p: TileProvider) => void;
dangerCells: DangerCell[];
recentActivity: ActivityItem[];
}
@@ -53,14 +50,12 @@ export function SidebarPanels({
isBusy,
locationHint,
showLocationCta,
hasLocation,
tileProvider,
setTileProvider,
dangerCells,
recentActivity,
}: SidebarPanelsProps) {
const { i18n, t } = useTranslation();
const locale = i18n.language === "fr" ? "fr-FR" : "en-US";
const hasLocation = useAppStore(state => Boolean(state.userLocation));
return (
<ScrollArea className="h-full">
@@ -80,7 +75,7 @@ export function SidebarPanels({
/>
<HotspotStatsPanel hasLocation={hasLocation} radiusKm={1} locationHint={locationHint} cells={dangerCells} />
<ActivityPanel items={recentActivity} emptyMessage={t("activity.empty")} />
<MapTilesPanel tileProvider={tileProvider} setTileProvider={setTileProvider} />
<MapTilesPanel />
</div>
</ScrollArea>
);
@@ -6,14 +6,12 @@ import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import type { TileProvider } from "@/hooks/use-leaflet-heatmap";
import { useAppStore } from "@/store/use-app-store";
interface MapTilesPanelProps {
tileProvider: TileProvider;
setTileProvider: (provider: TileProvider) => void;
}
export function MapTilesPanel({ tileProvider, setTileProvider }: MapTilesPanelProps) {
export function MapTilesPanel() {
const { t } = useTranslation();
const tileProvider = useAppStore(state => state.tileProvider);
const setTileProvider = useAppStore(state => state.setTileProvider);
const tileOptions = useMemo(
() => [
{ value: "openstreetmap" as TileProvider, label: t("map.tiles.openstreetmap") },
+1
View File
@@ -2,3 +2,4 @@
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
CORS_ALLOW_ORIGIN='^https?://(?:localhost|127\\.0\\.0\\.1)(?::[0-9]+)?$'
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
+4 -1
View File
@@ -45,7 +45,10 @@ class SignalController
'clientKey' => $clientKey,
'point' => [
'id' => $signal->id->toString(),
'signalLocation' => $signal->signalLocation,
'signalLocation' => [
'lat' => $signal->signalLocation->getLat(),
'lng' => $signal->signalLocation->getLng(),
],
'createdAt' => $signal->createdAt->format(DATE_ATOM),
'userKey' => $signal->userKey,
],
+4 -1
View File
@@ -38,7 +38,10 @@ class SignalSnapshotBuilder
foreach ($signals as $signal) {
$point = [
'id' => $signal->id->toString(),
'signalLocation' => $signal->signalLocation,
'signalLocation' => [
'lat' => $signal->signalLocation->getLat(),
'lng' => $signal->signalLocation->getLng(),
],
'createdAt' => $signal->createdAt->format(DATE_ATOM),
'userKey' => $signal->userKey,
];