refactor: add formatList

This commit is contained in:
ZhaiSoul 2025-11-06 17:20:04 +00:00
parent 5e6ffd62a3
commit 05a607b538
3 changed files with 51 additions and 34 deletions

View File

@ -1,25 +1,7 @@
import { TrackingDetailsSequence } from "@/types/timeline"; import { TrackingDetailsSequence } from "@/types/timeline";
import { t } from "i18next"; import { t } from "i18next";
import { getTranslatedLabel } from "./i18n"; import { getTranslatedLabel } from "./i18n";
import { capitalizeFirstLetter } from "./stringUtil"; import { capitalizeFirstLetter, formatList } from "./stringUtil";
function formatZonesList(zones: string[]): string {
if (zones.length === 0) return "";
if (zones.length === 1) return zones[0];
if (zones.length === 2) {
return t("list.two", {
0: zones[0],
1: zones[1],
});
}
const separatorWithSpace = t("list.separatorWithSpace", { ns: "common" });
const allButLast = zones.slice(0, -1).join(separatorWithSpace);
return t("list.many", {
items: allButLast,
last: zones[zones.length - 1],
});
}
export function getLifecycleItemDescription( export function getLifecycleItemDescription(
lifecycleItem: TrackingDetailsSequence, lifecycleItem: TrackingDetailsSequence,
@ -42,7 +24,7 @@ export function getLifecycleItemDescription(
return t("trackingDetails.lifecycleItemDesc.entered_zone", { return t("trackingDetails.lifecycleItemDesc.entered_zone", {
ns: "views/explore", ns: "views/explore",
label, label,
zones: formatZonesList( zones: formatList(
lifecycleItem.data.zones_friendly_names ?? lifecycleItem.data.zones, lifecycleItem.data.zones_friendly_names ?? lifecycleItem.data.zones,
), ),
}); });

View File

@ -1,3 +1,5 @@
import { t } from "i18next";
export const capitalizeFirstLetter = (text: string): string => { export const capitalizeFirstLetter = (text: string): string => {
return text.charAt(0).toUpperCase() + text.slice(1); return text.charAt(0).toUpperCase() + text.slice(1);
}; };
@ -45,3 +47,29 @@ export function generateFixedHash(name: string, prefix: string = "id"): string {
export function isValidId(name: string): boolean { export function isValidId(name: string): boolean {
return /^[a-zA-Z0-9_-]+$/.test(name) && !/^\d+$/.test(name); return /^[a-zA-Z0-9_-]+$/.test(name) && !/^\d+$/.test(name);
} }
/**
* Formats a list of strings into a human-readable format with proper localization.
* Handles different cases for empty, single-item, two-item, and multi-item lists.
*
* @param item - The array of strings to format
* @returns A formatted string representation of the list
*/
export function formatList(item: string[]): string {
if (item.length === 0) return "";
if (item.length === 1) return item[0];
if (item.length === 2) {
return t("list.two", {
0: item[0],
1: item[1],
ns: "common",
});
}
const separatorWithSpace = t("list.separatorWithSpace", { ns: "common" });
const allButLast = item.slice(0, -1).join(separatorWithSpace);
return t("list.many", {
items: allButLast,
last: item[item.length - 1],
});
}

View File

@ -42,6 +42,7 @@ import { IoMdArrowRoundBack } from "react-icons/io";
import { isDesktop } from "react-device-detect"; import { isDesktop } from "react-device-detect";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name"; import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
import { resolveZoneName } from "@/hooks/use-zone-friendly-name"; import { resolveZoneName } from "@/hooks/use-zone-friendly-name";
import { formatList } from "@/utils/stringUtil";
type CameraSettingsViewProps = { type CameraSettingsViewProps = {
selectedCamera: string; selectedCamera: string;
@ -106,27 +107,27 @@ export default function CameraSettingsView({
const alertsLabels = useMemo(() => { const alertsLabels = useMemo(() => {
return cameraConfig?.review.alerts.labels return cameraConfig?.review.alerts.labels
? cameraConfig.review.alerts.labels ? formatList(
.map((label) => cameraConfig.review.alerts.labels.map((label) =>
getTranslatedLabel( getTranslatedLabel(
label, label,
cameraConfig?.audio?.listen?.includes(label) ? "audio" : "object", cameraConfig?.audio?.listen?.includes(label) ? "audio" : "object",
), ),
) ),
.join(", ") )
: ""; : "";
}, [cameraConfig]); }, [cameraConfig]);
const detectionsLabels = useMemo(() => { const detectionsLabels = useMemo(() => {
return cameraConfig?.review.detections.labels return cameraConfig?.review.detections.labels
? cameraConfig.review.detections.labels ? formatList(
.map((label) => cameraConfig.review.detections.labels.map((label) =>
getTranslatedLabel( getTranslatedLabel(
label, label,
cameraConfig?.audio?.listen?.includes(label) ? "audio" : "object", cameraConfig?.audio?.listen?.includes(label) ? "audio" : "object",
), ),
) ),
.join(", ") )
: ""; : "";
}, [cameraConfig]); }, [cameraConfig]);
@ -555,8 +556,10 @@ export default function CameraSettingsView({
"cameraReview.reviewClassification.zoneObjectAlertsTips", "cameraReview.reviewClassification.zoneObjectAlertsTips",
{ {
alertsLabels, alertsLabels,
zone: watchedAlertsZones.map((zone) => zone: formatList(
getZoneName(zone), watchedAlertsZones.map((zone) =>
getZoneName(zone),
),
), ),
cameraName: selectCameraName, cameraName: selectCameraName,
}, },
@ -669,8 +672,10 @@ export default function CameraSettingsView({
i18nKey="cameraReview.reviewClassification.zoneObjectDetectionsTips.text" i18nKey="cameraReview.reviewClassification.zoneObjectDetectionsTips.text"
values={{ values={{
detectionsLabels, detectionsLabels,
zone: watchedDetectionsZones.map((zone) => zone: formatList(
getZoneName(zone), watchedDetectionsZones.map((zone) =>
getZoneName(zone),
),
), ),
cameraName: selectCameraName, cameraName: selectCameraName,
}} }}
@ -681,8 +686,10 @@ export default function CameraSettingsView({
i18nKey="cameraReview.reviewClassification.zoneObjectDetectionsTips.notSelectDetections" i18nKey="cameraReview.reviewClassification.zoneObjectDetectionsTips.notSelectDetections"
values={{ values={{
detectionsLabels, detectionsLabels,
zone: watchedDetectionsZones.map((zone) => zone: formatList(
getZoneName(zone), watchedDetectionsZones.map((zone) =>
getZoneName(zone),
),
), ),
cameraName: selectCameraName, cameraName: selectCameraName,
}} }}