Added the cost editor

This commit is contained in:
Colin Dawson 2026-02-24 22:40:37 +00:00
parent 083d79caaa
commit 066709eddb
27 changed files with 214 additions and 8 deletions

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Не прикажи",
"ShowAndEdit": "Прикажи и уреди",
"ShowOnly": "Прикажи само"
}

View File

@ -0,0 +1,5 @@
{
"High": "Висок",
"Low": "Нисак",
"Normal": "Нормално"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Nepokážte.",
"ShowAndEdit": "Zobrazit a upravit",
"ShowOnly": "Zobrazit pouze"
}

View File

@ -0,0 +1,5 @@
{
"High": "Vysoký",
"Low": "Nízký",
"Normal": "Normální"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Nicht anzeigen",
"ShowAndEdit": "Anzeigen und Bearbeiten",
"ShowOnly": "Nur anzeigen"
}

View File

@ -0,0 +1,4 @@
{
"High": "Hoch",
"Low": "Niedrig"
}

View File

@ -1,5 +1,5 @@
{
"DoNotShow": "Do Not Show",
"ShowOnly": "Show Only",
"ShowAndEdit": "Show And Edit"
}
"ShowAndEdit": "Show And Edit",
"ShowOnly": "Show Only"
}

View File

@ -1,5 +1,5 @@
{
"High": "High",
"Low": "Low",
"Normal": "Normal",
"High": "High"
}
"Normal": "Normal"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "No mostrar",
"ShowAndEdit": "Mostrar y editar",
"ShowOnly": "Mostrar solo"
}

View File

@ -0,0 +1,4 @@
{
"High": "Alto",
"Low": "Bajo"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Ne pas afficher",
"ShowAndEdit": "Afficher et modifier",
"ShowOnly": "Afficher uniquement"
}

View File

@ -0,0 +1,4 @@
{
"High": "Élevé",
"Low": "Faible"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "표시하지 마세요",
"ShowAndEdit": "보기 및 편집",
"ShowOnly": "쇼 온리"
}

View File

@ -0,0 +1,4 @@
{
"Low": "로우",
"Normal": "정상"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Niet weergeven",
"ShowAndEdit": "Weergeven en bewerken",
"ShowOnly": "Alleen weergeven"
}

View File

@ -0,0 +1,5 @@
{
"High": "Hoog",
"Low": "Laag",
"Normal": "Normaal"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Nie wyświetlać",
"ShowAndEdit": "Pokaż i edytuj",
"ShowOnly": "Pokaż tylko"
}

View File

@ -0,0 +1,4 @@
{
"High": "Wysoki",
"Low": "Niski"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Não exibir",
"ShowAndEdit": "Mostrar e Editar",
"ShowOnly": "Mostrar apenas"
}

View File

@ -0,0 +1,5 @@
{
"High": "Alto",
"Low": "Baixo",
"Normal": "Normal."
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "Не відображати",
"ShowAndEdit": "Показати та редагувати",
"ShowOnly": "Показати тільки"
}

View File

@ -0,0 +1,5 @@
{
"High": "Високий",
"Low": "Низький",
"Normal": "Звичайний"
}

View File

@ -0,0 +1,5 @@
{
"DoNotShow": "न दिखाएं।",
"ShowAndEdit": "دکھائیں اور ترمیم کریں",
"ShowOnly": "صرف ذلك هو المطلوب."
}

View File

@ -0,0 +1,5 @@
{
"High": "بहुत ऊँचा",
"Low": "کम",
"Normal": "سामान्य"
}

View File

@ -0,0 +1,80 @@
import Input, { InputType } from "./Input";
export type CostValue = {
amount: number;
isoCurrencySymbol: string;
};
export interface CostEditorProps {
name: string;
value: CostValue;
amountLabel?: string;
currencyLabel?: string;
amountError?: string;
currencyError?: string;
onChange: (value: CostValue) => void;
}
function normalizeAmount(rawValue: string) {
if (rawValue.trim() === "") {
return 0;
}
const parsed = Number(rawValue);
return Number.isFinite(parsed) ? parsed : 0;
}
export default function CostEditor(props: CostEditorProps) {
const {
name,
value,
amountLabel,
currencyLabel,
amountError,
currencyError,
onChange,
} = props;
const currentValue: CostValue = {
amount: value?.amount ?? 0,
isoCurrencySymbol: value?.isoCurrencySymbol ?? "",
};
return (
<div className="flex">
<Input
includeLabel={false}
name={`${name}-amount`}
label={amountLabel ?? "Amount"}
title={amountLabel ?? "Amount"}
type={InputType.number}
value={currentValue.amount}
step={0.01}
error={amountError ?? ""}
onChange={(event) => {
const nextAmount = normalizeAmount(String(event.target.value));
onChange({
...currentValue,
amount: nextAmount,
});
}}
/>
<Input
includeLabel={false}
name={`${name}-currency`}
label={currencyLabel ?? "Currency"}
title={currencyLabel ?? "Currency"}
type={InputType.text}
value={currentValue.isoCurrencySymbol}
maxLength={3}
error={currencyError ?? ""}
onChange={(event) => {
onChange({
...currentValue,
isoCurrencySymbol: String(event.target.value).toUpperCase(),
});
}}
/>
</div>
);
}

View File

@ -4,6 +4,7 @@ import ErrorBlock from "./ErrorBlock";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
import { Checklist } from "./Checklist";
import CostEditor, { CostValue } from "./CostEditor";
import { TagsInput } from "./inputTags";
export enum InputType {
@ -11,6 +12,7 @@ export enum InputType {
checkbox = "checkbox",
checklist = "checklist",
color = "color",
cost = "cost",
date = "date",
datetimelocal = "datetime-local",
email = "email",
@ -42,7 +44,7 @@ export interface InputProps {
placeHolder?: string;
readOnly?: boolean;
type: InputType;
value?: string | number | readonly string[] | boolean | undefined;
value?: string | number | readonly string[] | boolean | CostValue | undefined;
defaultValue?: string | number | readonly string[] | undefined;
min?: number;
max?: number;
@ -147,6 +149,18 @@ function Input(props: InputProps) {
error={props.error}
/>
),
[InputType.cost]: (props) => (
<CostEditor
name={name}
value={(value ?? { amount: 0, isoCurrencySymbol: "" }) as CostValue}
amountError={error}
onChange={(nextValue) => {
onChange?.({
target: { value: nextValue },
} as any);
}}
/>
),
// future controls go here
};

View File

@ -28,7 +28,14 @@ export const BudgetEditor: React.FC<CapabilityEditorProps> = (props) => {
InputType.number,
fieldErrors,
)}
<div>Cost goes here</div>
{renderTaskField(
task,
onChange,
"cost",
"Cost",
InputType.cost,
fieldErrors,
)}
<BudgetOptionPicker
includeLabel={true}
name="budgetOption"