Skip to content

Commit b61c236

Browse files
committed
fix(cms): refactor form field to 6 columns numeric
closed COD-296
1 parent 47ad2c1 commit b61c236

10 files changed

Lines changed: 6124 additions & 67 deletions

File tree

apps/cms/src/migrations/20250328_154057_cod_296.json

Lines changed: 5898 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { MigrateDownArgs, MigrateUpArgs, sql } from '@payloadcms/db-postgres';
2+
3+
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
4+
await db.execute(sql`
5+
-- Add temporary numeric columns
6+
ALTER TABLE "forms_blocks_checkbox" ADD COLUMN "width_numeric" numeric;
7+
ALTER TABLE "forms_blocks_country" ADD COLUMN "width_numeric" numeric;
8+
ALTER TABLE "forms_blocks_email" ADD COLUMN "width_numeric" numeric;
9+
ALTER TABLE "forms_blocks_number" ADD COLUMN "width_numeric" numeric;
10+
ALTER TABLE "forms_blocks_select" ADD COLUMN "width_numeric" numeric;
11+
ALTER TABLE "forms_blocks_text" ADD COLUMN "width_numeric" numeric;
12+
ALTER TABLE "forms_blocks_textarea" ADD COLUMN "width_numeric" numeric;
13+
14+
-- Convert enum values to numeric, capping at 6
15+
UPDATE "forms_blocks_checkbox" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
16+
UPDATE "forms_blocks_country" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
17+
UPDATE "forms_blocks_email" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
18+
UPDATE "forms_blocks_number" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
19+
UPDATE "forms_blocks_select" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
20+
UPDATE "forms_blocks_text" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
21+
UPDATE "forms_blocks_textarea" SET "width_numeric" = LEAST(CAST(CAST("width" AS text) AS numeric), 6);
22+
23+
-- Drop the original enum columns
24+
ALTER TABLE "forms_blocks_checkbox" DROP COLUMN "width";
25+
ALTER TABLE "forms_blocks_country" DROP COLUMN "width";
26+
ALTER TABLE "forms_blocks_email" DROP COLUMN "width";
27+
ALTER TABLE "forms_blocks_number" DROP COLUMN "width";
28+
ALTER TABLE "forms_blocks_select" DROP COLUMN "width";
29+
ALTER TABLE "forms_blocks_text" DROP COLUMN "width";
30+
ALTER TABLE "forms_blocks_textarea" DROP COLUMN "width";
31+
32+
-- Rename the numeric columns to the original names
33+
ALTER TABLE "forms_blocks_checkbox" RENAME COLUMN "width_numeric" TO "width";
34+
ALTER TABLE "forms_blocks_country" RENAME COLUMN "width_numeric" TO "width";
35+
ALTER TABLE "forms_blocks_email" RENAME COLUMN "width_numeric" TO "width";
36+
ALTER TABLE "forms_blocks_number" RENAME COLUMN "width_numeric" TO "width";
37+
ALTER TABLE "forms_blocks_select" RENAME COLUMN "width_numeric" TO "width";
38+
ALTER TABLE "forms_blocks_text" RENAME COLUMN "width_numeric" TO "width";
39+
ALTER TABLE "forms_blocks_textarea" RENAME COLUMN "width_numeric" TO "width";
40+
41+
-- Drop the now-unused enum types
42+
DROP TYPE "public"."enum_forms_blocks_checkbox_width";
43+
DROP TYPE "public"."enum_forms_blocks_country_width";
44+
DROP TYPE "public"."enum_forms_blocks_email_width";
45+
DROP TYPE "public"."enum_forms_blocks_number_width";
46+
DROP TYPE "public"."enum_forms_blocks_select_width";
47+
DROP TYPE "public"."enum_forms_blocks_text_width";
48+
DROP TYPE "public"."enum_forms_blocks_textarea_width";`);
49+
}
50+
51+
export async function down({
52+
db,
53+
payload,
54+
req
55+
}: MigrateDownArgs): Promise<void> {
56+
await db.execute(sql`
57+
-- First recreate the enum types
58+
CREATE TYPE "public"."enum_forms_blocks_checkbox_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
59+
CREATE TYPE "public"."enum_forms_blocks_country_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
60+
CREATE TYPE "public"."enum_forms_blocks_email_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
61+
CREATE TYPE "public"."enum_forms_blocks_number_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
62+
CREATE TYPE "public"."enum_forms_blocks_select_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
63+
CREATE TYPE "public"."enum_forms_blocks_text_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
64+
CREATE TYPE "public"."enum_forms_blocks_textarea_width" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12');
65+
66+
-- Add temporary enum columns
67+
ALTER TABLE "forms_blocks_checkbox" ADD COLUMN "width_enum" enum_forms_blocks_checkbox_width;
68+
ALTER TABLE "forms_blocks_country" ADD COLUMN "width_enum" enum_forms_blocks_country_width;
69+
ALTER TABLE "forms_blocks_email" ADD COLUMN "width_enum" enum_forms_blocks_email_width;
70+
ALTER TABLE "forms_blocks_number" ADD COLUMN "width_enum" enum_forms_blocks_number_width;
71+
ALTER TABLE "forms_blocks_select" ADD COLUMN "width_enum" enum_forms_blocks_select_width;
72+
ALTER TABLE "forms_blocks_text" ADD COLUMN "width_enum" enum_forms_blocks_text_width;
73+
ALTER TABLE "forms_blocks_textarea" ADD COLUMN "width_enum" enum_forms_blocks_textarea_width;
74+
75+
-- Convert numeric values to enum, ensuring they are valid enum values
76+
UPDATE "forms_blocks_checkbox" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_checkbox_width);
77+
UPDATE "forms_blocks_country" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_country_width);
78+
UPDATE "forms_blocks_email" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_email_width);
79+
UPDATE "forms_blocks_number" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_number_width);
80+
UPDATE "forms_blocks_select" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_select_width);
81+
UPDATE "forms_blocks_text" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_text_width);
82+
UPDATE "forms_blocks_textarea" SET "width_enum" = CAST(CAST(LEAST("width", 6) AS text) AS enum_forms_blocks_textarea_width);
83+
84+
-- Drop the numeric columns
85+
ALTER TABLE "forms_blocks_checkbox" DROP COLUMN "width";
86+
ALTER TABLE "forms_blocks_country" DROP COLUMN "width";
87+
ALTER TABLE "forms_blocks_email" DROP COLUMN "width";
88+
ALTER TABLE "forms_blocks_number" DROP COLUMN "width";
89+
ALTER TABLE "forms_blocks_select" DROP COLUMN "width";
90+
ALTER TABLE "forms_blocks_text" DROP COLUMN "width";
91+
ALTER TABLE "forms_blocks_textarea" DROP COLUMN "width";
92+
93+
-- Rename the enum columns to the original names
94+
ALTER TABLE "forms_blocks_checkbox" RENAME COLUMN "width_enum" TO "width";
95+
ALTER TABLE "forms_blocks_country" RENAME COLUMN "width_enum" TO "width";
96+
ALTER TABLE "forms_blocks_email" RENAME COLUMN "width_enum" TO "width";
97+
ALTER TABLE "forms_blocks_number" RENAME COLUMN "width_enum" TO "width";
98+
ALTER TABLE "forms_blocks_select" RENAME COLUMN "width_enum" TO "width";
99+
ALTER TABLE "forms_blocks_text" RENAME COLUMN "width_enum" TO "width";
100+
ALTER TABLE "forms_blocks_textarea" RENAME COLUMN "width_enum" TO "width";
101+
102+
-- Set default values
103+
ALTER TABLE "forms_blocks_checkbox" ALTER COLUMN "width" SET DEFAULT '12';
104+
ALTER TABLE "forms_blocks_country" ALTER COLUMN "width" SET DEFAULT '12';
105+
ALTER TABLE "forms_blocks_email" ALTER COLUMN "width" SET DEFAULT '12';
106+
ALTER TABLE "forms_blocks_number" ALTER COLUMN "width" SET DEFAULT '12';
107+
ALTER TABLE "forms_blocks_select" ALTER COLUMN "width" SET DEFAULT '12';
108+
ALTER TABLE "forms_blocks_text" ALTER COLUMN "width" SET DEFAULT '12';
109+
ALTER TABLE "forms_blocks_textarea" ALTER COLUMN "width" SET DEFAULT '12';`);
110+
}

apps/cms/src/migrations/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as migration_20250314_143517_cod_280 from './20250314_143517_cod_280';
99
import * as migration_20250317_105827_cod_278 from './20250317_105827_cod_278';
1010
import * as migration_20250325_141317_cod_282 from './20250325_141317_cod_282';
1111
import * as migration_20250326_200110_cod_282_2 from './20250326_200110_cod_282_2';
12+
import * as migration_20250328_154057_cod_296 from './20250328_154057_cod_296';
1213

1314
export const migrations = [
1415
{
@@ -65,5 +66,10 @@ export const migrations = [
6566
up: migration_20250326_200110_cod_282_2.up,
6667
down: migration_20250326_200110_cod_282_2.down,
6768
name: '20250326_200110_cod_282_2'
69+
},
70+
{
71+
up: migration_20250328_154057_cod_296.up,
72+
down: migration_20250328_154057_cod_296.down,
73+
name: '20250328_154057_cod_296'
6874
}
6975
];

libs/app-cms/util/plugins/src/lib/plugins/forms/customized-fields.ts

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,25 @@ import { type Block, Field } from 'payload';
44
/**
55
* Customized width field for form builder.
66
*
7-
* Replace the default width field with a select field
8-
* to allow selecting the width from a list of predefined values.
7+
* Replace the default width field with a number field
8+
* to allow entering the width in number of columns 1-6.
9+
*
10+
* **Important!**
11+
*
12+
* This solution is proof-of-concept and could be subject to change.
13+
*
14+
* We avoid using a select field since this will create database enums and contraints,
15+
* which makes it harder to migrate in case this logic needs to be changed.
916
*/
1017
const widthSelect: Field = {
1118
name: 'width',
12-
type: 'select',
13-
interfaceName: 'FieldWidth',
19+
type: 'number',
1420
label: 'Field width',
15-
options: [
16-
{ label: '1', value: '1' },
17-
{ label: '2', value: '2' },
18-
{ label: '3', value: '3' },
19-
{ label: '4', value: '4' },
20-
{ label: '5', value: '5' },
21-
{ label: '6', value: '6' },
22-
{ label: '7', value: '7' },
23-
{ label: '8', value: '8' },
24-
{ label: '9', value: '9' },
25-
{ label: '10', value: '10' },
26-
{ label: '11', value: '11' },
27-
{ label: '12 (full width)', value: '12' }
28-
],
29-
defaultValue: '12',
21+
min: 1,
22+
max: 6,
3023
admin: {
31-
description: 'Set the width of the field in the 12 column grid layout.'
24+
description:
25+
'Set number of columns the field should span (defaults to 6 = full width).'
3226
}
3327
};
3428

libs/shared/ui/payload-components/src/lib/blocks/FormBlock.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import { countryOptions } from '../form-items/country-options';
2525
import { Input } from '../form-items/Input';
2626
import { Select } from '../form-items/Select';
2727
import { Textarea } from '../form-items/Textarea';
28-
import ColSpan from '../layout/ColSpan';
28+
import { ColSpan } from '../layout/ColSpan';
29+
import { Grid } from '../layout/Grid';
2930
import { usePayload } from '../providers/PayloadProvider';
3031

3132
import { RichText } from './RichText';
@@ -183,27 +184,23 @@ export const FormBlock: React.FC<Props> = ({
183184
<Form {...form}>
184185
<form
185186
onSubmit={form.handleSubmit(onSubmit, onError)}
186-
className="mb-1 mr-1 space-y-6"
187+
className="mr-1 mb-1 space-y-6"
187188
>
188-
<div
189-
className={cn('grid grid-cols-12', {
190-
// Adding gap to a single grid cell overflows the container
191-
'gap-4': formBuilder.fields && formBuilder.fields.length > 1
192-
})}
193-
>
189+
{/* Grid columns must be in sync with forms plugin, width field */}
190+
<Grid columns={6}>
194191
{/* Loop through form builder field definitions */}
195192
{formBuilder?.fields?.map((fieldDef, index) => {
196193
// Handle message separately since it's not a form field.
197194
// It can be used to display a message to the user anywhere in the form.
198195
if (fieldDef.blockType === 'message') {
199196
return (
200197
fieldDef.message && (
201-
<div
202-
className="col-span-12 my-4 border-l-4 bg-inherit p-3 last:mb-0"
198+
<ColSpan
199+
className="my-4 border-l-4 bg-inherit p-3 last:mb-0"
203200
key={index}
204201
>
205202
<RichText data={fieldDef.message} disableProse={true} />
206-
</div>
203+
</ColSpan>
207204
)
208205
);
209206
}
@@ -280,9 +277,9 @@ export const FormBlock: React.FC<Props> = ({
280277
</ColSpan>
281278
);
282279
})}
283-
</div>
280+
</Grid>
284281

285-
<Button type="submit" isLoading={isLoading}>
282+
<Button type="submit" isLoading={isLoading} className="mt-4">
286283
{submitButtonLabel}
287284
</Button>
288285
</form>

libs/shared/ui/payload-components/src/lib/layout/ColSpan.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { FieldWidth } from '@codeware/shared/util/payload-types';
21
import { cn } from '@codeware/shared/util/ui';
32
import React from 'react';
43

@@ -10,7 +9,7 @@ type Props = {
109
*
1110
* Defaults to full width.
1211
*/
13-
columns?: FieldWidth;
12+
columns?: number | null;
1413
};
1514

1615
/**
@@ -31,18 +30,18 @@ export const ColSpan: React.FC<Props> = ({
3130
className={cn(
3231
'col-span-full',
3332
{
34-
'col-span-1': width === '1',
35-
'col-span-2': width === '2',
36-
'col-span-3': width === '3',
37-
'col-span-4': width === '4',
38-
'col-span-5': width === '5',
39-
'col-span-6': width === '6',
40-
'col-span-7': width === '7',
41-
'col-span-8': width === '8',
42-
'col-span-9': width === '9',
43-
'col-span-10': width === '10',
44-
'col-span-11': width === '11',
45-
'col-span-12': width === '12'
33+
'col-span-1': width === 1,
34+
'col-span-2': width === 2,
35+
'col-span-3': width === 3,
36+
'col-span-4': width === 4,
37+
'col-span-5': width === 5,
38+
'col-span-6': width === 6,
39+
'col-span-7': width === 7,
40+
'col-span-8': width === 8,
41+
'col-span-9': width === 9,
42+
'col-span-10': width === 10,
43+
'col-span-11': width === 11,
44+
'col-span-12': width === 12
4645
},
4746
className
4847
)}
@@ -51,5 +50,3 @@ export const ColSpan: React.FC<Props> = ({
5150
</div>
5251
);
5352
};
54-
55-
export default ColSpan;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { cn } from '@codeware/shared/util/ui';
2+
import React from 'react';
3+
4+
type Props = {
5+
children: React.ReactNode;
6+
className?: string;
7+
/**
8+
* The number of grid columns to use.
9+
*/
10+
columns: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
11+
};
12+
13+
/**
14+
* Wrapper component to dynamically define the number of grid columns.
15+
*
16+
* Useful when you can't use Tailwind classes to set a known value.
17+
*/
18+
export const Grid: React.FC<Props> = ({ children, className, columns }) => {
19+
return (
20+
<div
21+
className={cn(
22+
'grid',
23+
{
24+
'grid-cols-1': columns === 1,
25+
'grid-cols-2': columns === 2,
26+
'grid-cols-3': columns === 3,
27+
'grid-cols-4': columns === 4,
28+
'grid-cols-5': columns === 5,
29+
'grid-cols-6': columns === 6,
30+
'grid-cols-7': columns === 7,
31+
'grid-cols-8': columns === 8,
32+
'grid-cols-9': columns === 9,
33+
'grid-cols-10': columns === 10,
34+
'grid-cols-11': columns === 11,
35+
'grid-cols-12': columns === 12
36+
},
37+
className
38+
)}
39+
>
40+
{children}
41+
</div>
42+
);
43+
};

libs/shared/ui/react-components/src/lib/Code.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const Code: React.FC<Props> = ({
3636
>
3737
{tokens.map((line, i) => (
3838
<div key={i} {...getLineProps({ className: 'table-row', line })}>
39-
<span className="table-cell select-none text-right text-slate-400 dark:text-slate-500">
39+
<span className="table-cell text-right text-slate-400 select-none dark:text-slate-500">
4040
{i + 1}
4141
</span>
4242
<span className="table-cell pl-4">

libs/shared/ui/react-components/src/lib/CopyButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const CopyButton = ({ code, className = '' }: CopyButtonProps) => {
2626
return (
2727
<button
2828
onClick={handleCopy}
29-
className={`absolute right-2 top-2 rounded-md p-2 text-slate-500 transition-colors hover:bg-slate-200 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100 ${className}`}
29+
className={`absolute top-2 right-2 rounded-md p-2 text-slate-500 transition-colors hover:bg-slate-200 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100 ${className}`}
3030
aria-label="Copy code"
3131
>
3232
<div className="relative h-4 w-4">

0 commit comments

Comments
 (0)