Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 530ff2d1e4 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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") },
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user