diff --git a/apps/api/prisma/migrations/20230920140633_status_value_text_color/migration.sql b/apps/api/prisma/migrations/20230920140633_status_value_text_color/migration.sql new file mode 100644 index 000000000..6e4840753 --- /dev/null +++ b/apps/api/prisma/migrations/20230920140633_status_value_text_color/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "StatusValue" ADD COLUMN "textColor" TEXT; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 4730f332a..89415b9b7 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -1091,6 +1091,7 @@ model StatusValue { shouldDo ShouldDoType @default(SET_STATUS) whatPages WhatPages[] color String? + textColor String? type StatusValueType @default(STATUS_CODE) departments DepartmentValue[] diff --git a/apps/api/src/controllers/admin/values/import-values-controller.ts b/apps/api/src/controllers/admin/values/import-values-controller.ts index 6a7f64836..e11bc4367 100644 --- a/apps/api/src/controllers/admin/values/import-values-controller.ts +++ b/apps/api/src/controllers/admin/values/import-values-controller.ts @@ -294,6 +294,7 @@ export const typeHandlers = { ...makePrismaData(ValueType.CODES_10, { type: item.type as StatusValueType, color: item.color, + textColor: item.textColor, shouldDo: item.shouldDo as ShouldDoType, whatPages: whatPages as WhatPages[], value: item.value, diff --git a/apps/client/locales/en/values.json b/apps/client/locales/en/values.json index 49a09d050..94094b9ea 100644 --- a/apps/client/locales/en/values.json +++ b/apps/client/locales/en/values.json @@ -4,6 +4,7 @@ "values": "Values", "shouldDo": "Should Do", "color": "Color", + "textColor": "Text Color", "callsign": "Callsign", "department": "Department", "gameHash": "Game Hash", diff --git a/apps/client/src/components/admin/values/ManageValueModal.tsx b/apps/client/src/components/admin/values/ManageValueModal.tsx index 0d1a9394a..b622d6bdb 100644 --- a/apps/client/src/components/admin/values/ManageValueModal.tsx +++ b/apps/client/src/components/admin/values/ManageValueModal.tsx @@ -106,6 +106,7 @@ function createInitialValues(options: CreateInitialValuesOptions) { shouldDo: value && isStatusValue(value) ? value.shouldDo : "", color: value && isStatusValue(value) ? value.color ?? "" : "", + textColor: value && isStatusValue(value) ? value.textColor ?? "" : "", type: getTypeForValue(options.type, value), departments: value && @@ -281,6 +282,13 @@ export function ManageValueModal({ onCreate, onUpdate, type, value }: Props) { }; } + if (values.textColor && !hexColor().test(values.textColor)) { + return { + ...errors, + textColor: tValues("mustBeValidHexColor"), + }; + } + return errors; } diff --git a/apps/client/src/components/admin/values/manage-modal/status-value-fields.tsx b/apps/client/src/components/admin/values/manage-modal/status-value-fields.tsx index 36ae402d1..a8c5c3763 100644 --- a/apps/client/src/components/admin/values/manage-modal/status-value-fields.tsx +++ b/apps/client/src/components/admin/values/manage-modal/status-value-fields.tsx @@ -9,6 +9,7 @@ import { useValues } from "context/ValuesContext"; import { useTranslations } from "use-intl"; import { isOfficerRankValue } from "@snailycad/utils"; import { ManageValueFormValues } from "../ManageValueModal"; +import { generateContrastColor } from "lib/table/get-contrasting-text-color"; const HexColorPicker = dynamic(async () => (await import("react-colorful")).HexColorPicker); @@ -140,6 +141,31 @@ export function StatusValueFields() { + + + {values.showPicker ? ( + setFieldValue("textColor", textColor)} + style={{ width: "100%", height: "150px" }} + /> + ) : ( + + )} + + setFieldValue("showPicker", !values.showPicker)} + aria-label="Color Picker" + title="Color Picker" + > + + + + + setFieldValue("type", value)} @@ -148,6 +174,22 @@ export function StatusValueFields() { Status Code Situation Code + + + Color Preview + + + Lorem ipsum dolor sit. + + > ); } diff --git a/apps/client/src/components/dispatch/active-units/deputies/active-deputies.tsx b/apps/client/src/components/dispatch/active-units/deputies/active-deputies.tsx index 400da2f98..2281f36f4 100644 --- a/apps/client/src/components/dispatch/active-units/deputies/active-deputies.tsx +++ b/apps/client/src/components/dispatch/active-units/deputies/active-deputies.tsx @@ -107,17 +107,21 @@ function ActiveDeputies({ initialDeputies }: Props) { features={{ isWithinCardOrModal: true }} containerProps={{ className: "mb-3 px-4" }} data={activeDeputies.map((deputy) => { - const color = deputy.status?.color; - const useDot = user?.statusViewMode === StatusViewMode.DOT_COLOR; + const backgroundColor = deputy.status?.color; + const textColor = deputy.status?.textColor; + const color = backgroundColor + ? textColor || generateContrastColor(backgroundColor) + : textColor; + const useDot = user?.statusViewMode === StatusViewMode.DOT_COLOR; const nameAndCallsign = `${generateCallsign(deputy)} ${makeUnitName(deputy)}`; return { id: deputy.id, rowProps: { style: { - background: !useDot && color ? color : undefined, - color: !useDot && color ? generateContrastColor(color) : undefined, + backgroundColor: !useDot && backgroundColor ? backgroundColor : undefined, + color, }, }, name: nameAndCallsign, diff --git a/apps/client/src/components/dispatch/active-units/officers/active-officers.tsx b/apps/client/src/components/dispatch/active-units/officers/active-officers.tsx index 259344919..38d7b5efa 100644 --- a/apps/client/src/components/dispatch/active-units/officers/active-officers.tsx +++ b/apps/client/src/components/dispatch/active-units/officers/active-officers.tsx @@ -129,7 +129,11 @@ function ActiveOfficers({ initialOfficers }: Props) { containerProps={{ className: "mb-3 px-4" }} tableState={tableState} data={activeOfficers.map((officer) => { - const color = officer.status?.color; + const backgroundColor = officer.status?.color; + const textColor = officer.status?.textColor; + const color = backgroundColor + ? textColor || generateContrastColor(backgroundColor) + : textColor; const useDot = user?.statusViewMode === StatusViewMode.DOT_COLOR; const nameAndCallsign = `${generateCallsign(officer)} ${makeUnitName(officer)}`; @@ -138,8 +142,8 @@ function ActiveOfficers({ initialOfficers }: Props) { id: officer.id, rowProps: { style: { - background: !useDot && color ? color : undefined, - color: !useDot && color ? generateContrastColor(color) : undefined, + backgroundColor: !useDot && backgroundColor ? backgroundColor : undefined, + color, }, }, name: nameAndCallsign, @@ -171,9 +175,9 @@ function ActiveOfficers({ initialOfficers }: Props) { rank: (isUnitOfficer(officer) && officer.rank?.value) ?? common("none"), status: ( - {useDot && color ? ( + {useDot && backgroundColor ? ( ) : null} diff --git a/packages/schemas/src/admin/values/import.ts b/packages/schemas/src/admin/values/import.ts index b021b39f0..fed2e0f74 100644 --- a/packages/schemas/src/admin/values/import.ts +++ b/packages/schemas/src/admin/values/import.ts @@ -36,6 +36,7 @@ const TYPE_REGEX = /STATUS_CODE|SITUATION_CODE/; export const CODES_10_SCHEMA = BASE_VALUE_SCHEMA.extend({ shouldDo: z.string().regex(SHOULD_DO_REGEX), color: z.string().max(255).nullish(), + textColor: z.string().max(255).nullish(), type: z.string().regex(TYPE_REGEX).max(255), whatPages: z.array(z.any()).max(3).nullish(), departments: z.array(z.any()).nullish(),