refactor: improve code formatting and readability in page.tsx
This commit is contained in:
@@ -580,17 +580,15 @@ const formatCustomFieldDetailValue = (
|
|||||||
const trimmed = value.trim();
|
const trimmed = value.trim();
|
||||||
if (!trimmed) return "—";
|
if (!trimmed) return "—";
|
||||||
try {
|
try {
|
||||||
// Validate URL before rendering as a link.
|
const parsedUrl = new URL(trimmed);
|
||||||
// eslint-disable-next-line no-new
|
|
||||||
new URL(trimmed);
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={trimmed}
|
href={parsedUrl.toString()}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="inline-flex items-center gap-1 text-blue-700 underline decoration-blue-300 underline-offset-2 hover:text-blue-800"
|
className="inline-flex items-center gap-1 text-blue-700 underline decoration-blue-300 underline-offset-2 hover:text-blue-800"
|
||||||
>
|
>
|
||||||
<span className="break-all">{trimmed}</span>
|
<span className="break-all">{parsedUrl.toString()}</span>
|
||||||
<ArrowUpRight className="h-3.5 w-3.5 flex-shrink-0" />
|
<ArrowUpRight className="h-3.5 w-3.5 flex-shrink-0" />
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
@@ -601,8 +599,7 @@ const formatCustomFieldDetailValue = (
|
|||||||
|
|
||||||
if (fieldType === "json") {
|
if (fieldType === "json") {
|
||||||
try {
|
try {
|
||||||
const normalized =
|
const normalized = typeof value === "string" ? JSON.parse(value) : value;
|
||||||
typeof value === "string" ? JSON.parse(value) : value;
|
|
||||||
return (
|
return (
|
||||||
<pre className="whitespace-pre-wrap break-words rounded border border-slate-200 bg-white px-2 py-1 font-mono text-xs leading-relaxed text-slate-800">
|
<pre className="whitespace-pre-wrap break-words rounded border border-slate-200 bg-white px-2 py-1 font-mono text-xs leading-relaxed text-slate-800">
|
||||||
{JSON.stringify(normalized, null, 2)}
|
{JSON.stringify(normalized, null, 2)}
|
||||||
@@ -615,7 +612,11 @@ const formatCustomFieldDetailValue = (
|
|||||||
|
|
||||||
if (fieldType === "text_long") {
|
if (fieldType === "text_long") {
|
||||||
const text = customFieldInputText(value);
|
const text = customFieldInputText(value);
|
||||||
return text ? <span className="whitespace-pre-wrap break-words">{text}</span> : "—";
|
return text ? (
|
||||||
|
<span className="whitespace-pre-wrap break-words">{text}</span>
|
||||||
|
) : (
|
||||||
|
"—"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return customFieldInputText(value) || "—";
|
return customFieldInputText(value) || "—";
|
||||||
@@ -634,7 +635,8 @@ const isCustomFieldVisible = (
|
|||||||
value: unknown,
|
value: unknown,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (definition.ui_visibility === "hidden") return false;
|
if (definition.ui_visibility === "hidden") return false;
|
||||||
if (definition.ui_visibility === "if_set") return isCustomFieldValueSet(value);
|
if (definition.ui_visibility === "if_set")
|
||||||
|
return isCustomFieldValueSet(value);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -644,7 +646,10 @@ const parseCustomFieldInputValue = (
|
|||||||
): unknown | null => {
|
): unknown | null => {
|
||||||
const trimmed = text.trim();
|
const trimmed = text.trim();
|
||||||
if (!trimmed) return null;
|
if (!trimmed) return null;
|
||||||
if (definition.field_type === "text" || definition.field_type === "text_long") {
|
if (
|
||||||
|
definition.field_type === "text" ||
|
||||||
|
definition.field_type === "text_long"
|
||||||
|
) {
|
||||||
return trimmed;
|
return trimmed;
|
||||||
}
|
}
|
||||||
if (definition.field_type === "integer") {
|
if (definition.field_type === "integer") {
|
||||||
@@ -739,7 +744,10 @@ const customFieldPatchPayload = (
|
|||||||
): TaskCustomFieldValues =>
|
): TaskCustomFieldValues =>
|
||||||
definitions.reduce((acc, definition) => {
|
definitions.reduce((acc, definition) => {
|
||||||
const key = definition.field_key;
|
const key = definition.field_key;
|
||||||
const currentValue = Object.prototype.hasOwnProperty.call(currentValues, key)
|
const currentValue = Object.prototype.hasOwnProperty.call(
|
||||||
|
currentValues,
|
||||||
|
key,
|
||||||
|
)
|
||||||
? currentValues[key]
|
? currentValues[key]
|
||||||
: null;
|
: null;
|
||||||
const nextValue = Object.prototype.hasOwnProperty.call(nextValues, key)
|
const nextValue = Object.prototype.hasOwnProperty.call(nextValues, key)
|
||||||
@@ -2855,7 +2863,10 @@ export default function BoardDetailPage() {
|
|||||||
if (dueDateChanged) {
|
if (dueDateChanged) {
|
||||||
updatePayload.due_at = localDateInputToUtcIso(editDueDate);
|
updatePayload.due_at = localDateInputToUtcIso(editDueDate);
|
||||||
}
|
}
|
||||||
if (customFieldValuesChanged && Object.keys(editCustomFieldPatch).length > 0) {
|
if (
|
||||||
|
customFieldValuesChanged &&
|
||||||
|
Object.keys(editCustomFieldPatch).length > 0
|
||||||
|
) {
|
||||||
updatePayload.custom_field_values = editCustomFieldPatch;
|
updatePayload.custom_field_values = editCustomFieldPatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4343,7 +4354,8 @@ export default function BoardDetailPage() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{boardCustomFieldDefinitions.map((definition) => {
|
{boardCustomFieldDefinitions.map((definition) => {
|
||||||
const fieldValue = editCustomFieldValues[definition.field_key];
|
const fieldValue =
|
||||||
|
editCustomFieldValues[definition.field_key];
|
||||||
if (!isCustomFieldVisible(definition, fieldValue)) {
|
if (!isCustomFieldVisible(definition, fieldValue)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -4371,7 +4383,9 @@ export default function BoardDetailPage() {
|
|||||||
value === "unset" ? null : value === "true",
|
value === "unset" ? null : value === "true",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={!selectedTask || isSavingTask || !canWrite}
|
disabled={
|
||||||
|
!selectedTask || isSavingTask || !canWrite
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Optional" />
|
<SelectValue placeholder="Optional" />
|
||||||
@@ -4389,7 +4403,8 @@ export default function BoardDetailPage() {
|
|||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
setEditCustomFieldValues((prev) => ({
|
setEditCustomFieldValues((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[definition.field_key]: parseCustomFieldInputValue(
|
[definition.field_key]:
|
||||||
|
parseCustomFieldInputValue(
|
||||||
definition,
|
definition,
|
||||||
event.target.value,
|
event.target.value,
|
||||||
),
|
),
|
||||||
@@ -4402,7 +4417,9 @@ export default function BoardDetailPage() {
|
|||||||
: "Optional"
|
: "Optional"
|
||||||
}
|
}
|
||||||
rows={definition.field_type === "text_long" ? 3 : 4}
|
rows={definition.field_type === "text_long" ? 3 : 4}
|
||||||
disabled={!selectedTask || isSavingTask || !canWrite}
|
disabled={
|
||||||
|
!selectedTask || isSavingTask || !canWrite
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
@@ -4418,12 +4435,17 @@ export default function BoardDetailPage() {
|
|||||||
? "url"
|
? "url"
|
||||||
: "text"
|
: "text"
|
||||||
}
|
}
|
||||||
step={definition.field_type === "decimal" ? "any" : undefined}
|
step={
|
||||||
|
definition.field_type === "decimal"
|
||||||
|
? "any"
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
value={customFieldInputText(fieldValue)}
|
value={customFieldInputText(fieldValue)}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
setEditCustomFieldValues((prev) => ({
|
setEditCustomFieldValues((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[definition.field_key]: parseCustomFieldInputValue(
|
[definition.field_key]:
|
||||||
|
parseCustomFieldInputValue(
|
||||||
definition,
|
definition,
|
||||||
event.target.value,
|
event.target.value,
|
||||||
),
|
),
|
||||||
@@ -4435,7 +4457,9 @@ export default function BoardDetailPage() {
|
|||||||
? `Default: ${customFieldInputText(definition.default_value)}`
|
? `Default: ${customFieldInputText(definition.default_value)}`
|
||||||
: "Optional"
|
: "Optional"
|
||||||
}
|
}
|
||||||
disabled={!selectedTask || isSavingTask || !canWrite}
|
disabled={
|
||||||
|
!selectedTask || isSavingTask || !canWrite
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{definition.description ? (
|
{definition.description ? (
|
||||||
@@ -4833,7 +4857,8 @@ export default function BoardDetailPage() {
|
|||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
setCreateCustomFieldValues((prev) => ({
|
setCreateCustomFieldValues((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[definition.field_key]: parseCustomFieldInputValue(
|
[definition.field_key]:
|
||||||
|
parseCustomFieldInputValue(
|
||||||
definition,
|
definition,
|
||||||
event.target.value,
|
event.target.value,
|
||||||
),
|
),
|
||||||
@@ -4862,12 +4887,17 @@ export default function BoardDetailPage() {
|
|||||||
? "url"
|
? "url"
|
||||||
: "text"
|
: "text"
|
||||||
}
|
}
|
||||||
step={definition.field_type === "decimal" ? "any" : undefined}
|
step={
|
||||||
|
definition.field_type === "decimal"
|
||||||
|
? "any"
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
value={customFieldInputText(fieldValue)}
|
value={customFieldInputText(fieldValue)}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
setCreateCustomFieldValues((prev) => ({
|
setCreateCustomFieldValues((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[definition.field_key]: parseCustomFieldInputValue(
|
[definition.field_key]:
|
||||||
|
parseCustomFieldInputValue(
|
||||||
definition,
|
definition,
|
||||||
event.target.value,
|
event.target.value,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -67,7 +67,13 @@ type EditCustomFieldFormProps = {
|
|||||||
onSubmit: (updates: TaskCustomFieldDefinitionUpdate) => Promise<void>;
|
onSubmit: (updates: TaskCustomFieldDefinitionUpdate) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const STRING_FIELD_TYPES = new Set(["text", "text_long", "date", "date_time", "url"]);
|
const STRING_FIELD_TYPES = new Set([
|
||||||
|
"text",
|
||||||
|
"text_long",
|
||||||
|
"date",
|
||||||
|
"date_time",
|
||||||
|
"url",
|
||||||
|
]);
|
||||||
|
|
||||||
const parseDefaultValue = (
|
const parseDefaultValue = (
|
||||||
fieldType: FormState["fieldType"],
|
fieldType: FormState["fieldType"],
|
||||||
@@ -92,11 +98,14 @@ const parseDefaultValue = (
|
|||||||
}
|
}
|
||||||
if (fieldType === "boolean") {
|
if (fieldType === "boolean") {
|
||||||
if (trimmed.toLowerCase() === "true") return { value: true, error: null };
|
if (trimmed.toLowerCase() === "true") return { value: true, error: null };
|
||||||
if (trimmed.toLowerCase() === "false")
|
if (trimmed.toLowerCase() === "false") return { value: false, error: null };
|
||||||
return { value: false, error: null };
|
|
||||||
return { value: null, error: "Default value must be true or false." };
|
return { value: null, error: "Default value must be true or false." };
|
||||||
}
|
}
|
||||||
if (fieldType === "date" || fieldType === "date_time" || fieldType === "url") {
|
if (
|
||||||
|
fieldType === "date" ||
|
||||||
|
fieldType === "date_time" ||
|
||||||
|
fieldType === "url"
|
||||||
|
) {
|
||||||
return { value: trimmed, error: null };
|
return { value: trimmed, error: null };
|
||||||
}
|
}
|
||||||
if (fieldType === "json") {
|
if (fieldType === "json") {
|
||||||
@@ -198,7 +207,9 @@ function EditCustomFieldForm({
|
|||||||
trimmedValidationRegex &&
|
trimmedValidationRegex &&
|
||||||
!STRING_FIELD_TYPES.has(formState.fieldType)
|
!STRING_FIELD_TYPES.has(formState.fieldType)
|
||||||
) {
|
) {
|
||||||
setSaveError("Validation regex is only supported for string field types.");
|
setSaveError(
|
||||||
|
"Validation regex is only supported for string field types.",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const parsedDefaultValue = parseDefaultValue(
|
const parsedDefaultValue = parseDefaultValue(
|
||||||
@@ -455,7 +466,9 @@ function EditCustomFieldForm({
|
|||||||
Loading boards…
|
Loading boards…
|
||||||
</div>
|
</div>
|
||||||
) : boardsError ? (
|
) : boardsError ? (
|
||||||
<div className="px-4 py-6 text-sm text-rose-700">{boardsError}</div>
|
<div className="px-4 py-6 text-sm text-rose-700">
|
||||||
|
{boardsError}
|
||||||
|
</div>
|
||||||
) : filteredBoards.length === 0 ? (
|
) : filteredBoards.length === 0 ? (
|
||||||
<div className="px-4 py-6 text-sm text-slate-500">
|
<div className="px-4 py-6 text-sm text-slate-500">
|
||||||
No boards found.
|
No boards found.
|
||||||
|
|||||||
@@ -63,7 +63,13 @@ const defaultFormState: FormState = {
|
|||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const STRING_FIELD_TYPES = new Set(["text", "text_long", "date", "date_time", "url"]);
|
const STRING_FIELD_TYPES = new Set([
|
||||||
|
"text",
|
||||||
|
"text_long",
|
||||||
|
"date",
|
||||||
|
"date_time",
|
||||||
|
"url",
|
||||||
|
]);
|
||||||
|
|
||||||
const parseDefaultValue = (
|
const parseDefaultValue = (
|
||||||
fieldType: FormState["fieldType"],
|
fieldType: FormState["fieldType"],
|
||||||
@@ -88,11 +94,14 @@ const parseDefaultValue = (
|
|||||||
}
|
}
|
||||||
if (fieldType === "boolean") {
|
if (fieldType === "boolean") {
|
||||||
if (trimmed.toLowerCase() === "true") return { value: true, error: null };
|
if (trimmed.toLowerCase() === "true") return { value: true, error: null };
|
||||||
if (trimmed.toLowerCase() === "false")
|
if (trimmed.toLowerCase() === "false") return { value: false, error: null };
|
||||||
return { value: false, error: null };
|
|
||||||
return { value: null, error: "Default value must be true or false." };
|
return { value: null, error: "Default value must be true or false." };
|
||||||
}
|
}
|
||||||
if (fieldType === "date" || fieldType === "date_time" || fieldType === "url") {
|
if (
|
||||||
|
fieldType === "date" ||
|
||||||
|
fieldType === "date_time" ||
|
||||||
|
fieldType === "url"
|
||||||
|
) {
|
||||||
return { value: trimmed, error: null };
|
return { value: trimmed, error: null };
|
||||||
}
|
}
|
||||||
if (fieldType === "json") {
|
if (fieldType === "json") {
|
||||||
|
|||||||
Reference in New Issue
Block a user