@trustless-work/blocks 0.0.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. package/bin/index.js +263 -22
  2. package/package.json +1 -1
  3. package/templates/escrows/details/Actions.tsx +3 -0
  4. package/templates/escrows/details/MilestoneCard.tsx +2 -2
  5. package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +0 -2
  6. package/templates/escrows/multi-release/dispute-milestone/button/{DisputeEscrow.tsx → DisputeMilestone.tsx} +4 -4
  7. package/templates/escrows/multi-release/release-milestone/button/{ReleaseEscrow.tsx → ReleaseMilestone.tsx} +4 -4
  8. package/templates/escrows/single-multi-release/approve-milestone/dialog/ApproveMilestone.tsx +1 -1
  9. package/templates/escrows/single-multi-release/approve-milestone/form/ApproveMilestone.tsx +1 -1
  10. package/templates/escrows/single-multi-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +1 -1
  11. package/templates/escrows/single-multi-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +1 -1
  12. package/templates/escrows/single-multi-release/fund-escrow/dialog/FundEscrow.tsx +1 -1
  13. package/templates/escrows/single-multi-release/fund-escrow/form/FundEscrow.tsx +1 -1
  14. /package/templates/escrows/single-multi-release/approve-milestone/{schema.ts → shared/schema.ts} +0 -0
  15. /package/templates/escrows/single-multi-release/approve-milestone/{useApproveMilestone.ts → shared/useApproveMilestone.ts} +0 -0
  16. /package/templates/escrows/single-multi-release/change-milestone-status/{schema.ts → shared/schema.ts} +0 -0
  17. /package/templates/escrows/single-multi-release/change-milestone-status/{useChangeMilestoneStatus.ts → shared/useChangeMilestoneStatus.ts} +0 -0
  18. /package/templates/escrows/single-multi-release/fund-escrow/{schema.ts → shared/schema.ts} +0 -0
  19. /package/templates/escrows/single-multi-release/fund-escrow/{useFundEscrow.ts → shared/useFundEscrow.ts} +0 -0
package/bin/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /*
4
4
  AUTHOR: @trustless-work / Joel Vargas
@@ -855,6 +855,133 @@ function copyTemplate(name, { uiBase, shouldInstall = false } = {}) {
855
855
  );
856
856
  }
857
857
 
858
+ // Post-copy: materialize shared files for single-multi-release modules
859
+ try {
860
+ const isSingleMultiApproveRoot =
861
+ name === "escrows/single-multi-release/approve-milestone";
862
+ const isSingleMultiApproveDialog =
863
+ name === "escrows/single-multi-release/approve-milestone/dialog";
864
+ const isSingleMultiApproveForm =
865
+ name === "escrows/single-multi-release/approve-milestone/form";
866
+
867
+ const srcSharedDir = path.join(
868
+ TEMPLATES_DIR,
869
+ "escrows",
870
+ "single-multi-release",
871
+ "approve-milestone",
872
+ "shared"
873
+ );
874
+
875
+ function copySingleMultiApproveSharedInto(targetDir) {
876
+ if (!fs.existsSync(srcSharedDir)) return;
877
+ const entries = fs.readdirSync(srcSharedDir, { withFileTypes: true });
878
+ for (const entry of entries) {
879
+ if (!/\.(tsx?|jsx?)$/i.test(entry.name)) continue;
880
+ const entrySrc = path.join(srcSharedDir, entry.name);
881
+ const entryDest = path.join(targetDir, entry.name);
882
+ writeTransformed(entrySrc, entryDest);
883
+ }
884
+ }
885
+
886
+ if (isSingleMultiApproveRoot) {
887
+ copySingleMultiApproveSharedInto(path.join(destDir, "dialog"));
888
+ copySingleMultiApproveSharedInto(path.join(destDir, "form"));
889
+ } else if (isSingleMultiApproveDialog) {
890
+ copySingleMultiApproveSharedInto(destDir);
891
+ } else if (isSingleMultiApproveForm) {
892
+ copySingleMultiApproveSharedInto(destDir);
893
+ }
894
+ } catch (e) {
895
+ console.warn(
896
+ "⚠️ Failed to materialize shared single-multi-release approve-milestone files:",
897
+ e?.message || e
898
+ );
899
+ }
900
+
901
+ try {
902
+ const isSingleMultiChangeRoot =
903
+ name === "escrows/single-multi-release/change-milestone-status";
904
+ const isSingleMultiChangeDialog =
905
+ name === "escrows/single-multi-release/change-milestone-status/dialog";
906
+ const isSingleMultiChangeForm =
907
+ name === "escrows/single-multi-release/change-milestone-status/form";
908
+
909
+ const srcSharedDir = path.join(
910
+ TEMPLATES_DIR,
911
+ "escrows",
912
+ "single-multi-release",
913
+ "change-milestone-status",
914
+ "shared"
915
+ );
916
+
917
+ function copySingleMultiChangeSharedInto(targetDir) {
918
+ if (!fs.existsSync(srcSharedDir)) return;
919
+ const entries = fs.readdirSync(srcSharedDir, { withFileTypes: true });
920
+ for (const entry of entries) {
921
+ if (!/\.(tsx?|jsx?)$/i.test(entry.name)) continue;
922
+ const entrySrc = path.join(srcSharedDir, entry.name);
923
+ const entryDest = path.join(targetDir, entry.name);
924
+ writeTransformed(entrySrc, entryDest);
925
+ }
926
+ }
927
+
928
+ if (isSingleMultiChangeRoot) {
929
+ copySingleMultiChangeSharedInto(path.join(destDir, "dialog"));
930
+ copySingleMultiChangeSharedInto(path.join(destDir, "form"));
931
+ } else if (isSingleMultiChangeDialog) {
932
+ copySingleMultiChangeSharedInto(destDir);
933
+ } else if (isSingleMultiChangeForm) {
934
+ copySingleMultiChangeSharedInto(destDir);
935
+ }
936
+ } catch (e) {
937
+ console.warn(
938
+ "⚠️ Failed to materialize shared single-multi-release change-milestone-status files:",
939
+ e?.message || e
940
+ );
941
+ }
942
+
943
+ try {
944
+ const isSingleMultiFundRoot =
945
+ name === "escrows/single-multi-release/fund-escrow";
946
+ const isSingleMultiFundDialog =
947
+ name === "escrows/single-multi-release/fund-escrow/dialog";
948
+ const isSingleMultiFundForm =
949
+ name === "escrows/single-multi-release/fund-escrow/form";
950
+
951
+ const srcSharedDir = path.join(
952
+ TEMPLATES_DIR,
953
+ "escrows",
954
+ "single-multi-release",
955
+ "fund-escrow",
956
+ "shared"
957
+ );
958
+
959
+ function copySingleMultiFundSharedInto(targetDir) {
960
+ if (!fs.existsSync(srcSharedDir)) return;
961
+ const entries = fs.readdirSync(srcSharedDir, { withFileTypes: true });
962
+ for (const entry of entries) {
963
+ if (!/\.(tsx?|jsx?)$/i.test(entry.name)) continue;
964
+ const entrySrc = path.join(srcSharedDir, entry.name);
965
+ const entryDest = path.join(targetDir, entry.name);
966
+ writeTransformed(entrySrc, entryDest);
967
+ }
968
+ }
969
+
970
+ if (isSingleMultiFundRoot) {
971
+ copySingleMultiFundSharedInto(path.join(destDir, "dialog"));
972
+ copySingleMultiFundSharedInto(path.join(destDir, "form"));
973
+ } else if (isSingleMultiFundDialog) {
974
+ copySingleMultiFundSharedInto(destDir);
975
+ } else if (isSingleMultiFundForm) {
976
+ copySingleMultiFundSharedInto(destDir);
977
+ }
978
+ } catch (e) {
979
+ console.warn(
980
+ "⚠️ Failed to materialize shared single-multi-release fund-escrow files:",
981
+ e?.message || e
982
+ );
983
+ }
984
+
858
985
  // If adding the whole single-release bundle, materialize all shared files
859
986
  try {
860
987
  if (name === "escrows/single-release") {
@@ -942,6 +1069,48 @@ function copyTemplate(name, { uiBase, shouldInstall = false } = {}) {
942
1069
  );
943
1070
  }
944
1071
 
1072
+ // If adding the whole single-multi-release bundle, materialize all shared files
1073
+ try {
1074
+ if (name === "escrows/single-multi-release") {
1075
+ const modules = [
1076
+ "approve-milestone",
1077
+ "change-milestone-status",
1078
+ "fund-escrow",
1079
+ ];
1080
+
1081
+ for (const mod of modules) {
1082
+ const srcSharedDir = path.join(
1083
+ TEMPLATES_DIR,
1084
+ "escrows",
1085
+ "single-multi-release",
1086
+ mod,
1087
+ "shared"
1088
+ );
1089
+ if (!fs.existsSync(srcSharedDir)) continue;
1090
+
1091
+ const targets = [
1092
+ path.join(destDir, mod, "dialog"),
1093
+ path.join(destDir, mod, "form"),
1094
+ ];
1095
+
1096
+ const entries = fs.readdirSync(srcSharedDir, { withFileTypes: true });
1097
+ for (const entry of entries) {
1098
+ if (!/\.(tsx?|jsx?)$/i.test(entry.name)) continue;
1099
+ const entrySrc = path.join(srcSharedDir, entry.name);
1100
+ for (const t of targets) {
1101
+ const entryDest = path.join(t, entry.name);
1102
+ writeTransformed(entrySrc, entryDest);
1103
+ }
1104
+ }
1105
+ }
1106
+ }
1107
+ } catch (e) {
1108
+ console.warn(
1109
+ "⚠️ Failed to materialize shared files for single-multi-release bundle:",
1110
+ e?.message || e
1111
+ );
1112
+ }
1113
+
945
1114
  // If adding the root escrows bundle, also materialize single-release shared files
946
1115
  try {
947
1116
  if (name === "escrows") {
@@ -1030,6 +1199,49 @@ function copyTemplate(name, { uiBase, shouldInstall = false } = {}) {
1030
1199
  e?.message || e
1031
1200
  );
1032
1201
  }
1202
+
1203
+ // If adding the root escrows bundle, also materialize single-multi-release shared files
1204
+ try {
1205
+ if (name === "escrows") {
1206
+ const modules = [
1207
+ "approve-milestone",
1208
+ "change-milestone-status",
1209
+ "fund-escrow",
1210
+ ];
1211
+
1212
+ const baseTarget = path.join(destDir, "single-multi-release");
1213
+ for (const mod of modules) {
1214
+ const srcSharedDir = path.join(
1215
+ TEMPLATES_DIR,
1216
+ "escrows",
1217
+ "single-multi-release",
1218
+ mod,
1219
+ "shared"
1220
+ );
1221
+ if (!fs.existsSync(srcSharedDir)) continue;
1222
+
1223
+ const targets = [
1224
+ path.join(baseTarget, mod, "dialog"),
1225
+ path.join(baseTarget, mod, "form"),
1226
+ ];
1227
+
1228
+ const entries = fs.readdirSync(srcSharedDir, { withFileTypes: true });
1229
+ for (const entry of entries) {
1230
+ if (!/\.(tsx?|jsx?)$/i.test(entry.name)) continue;
1231
+ const entrySrc = path.join(srcSharedDir, entry.name);
1232
+ for (const t of targets) {
1233
+ const entryDest = path.join(t, entry.name);
1234
+ writeTransformed(entrySrc, entryDest);
1235
+ }
1236
+ }
1237
+ }
1238
+ }
1239
+ } catch (e) {
1240
+ console.warn(
1241
+ "⚠️ Failed to materialize shared files for escrows root (single-multi-release):",
1242
+ e?.message || e
1243
+ );
1244
+ }
1033
1245
  } else if (fs.existsSync(srcFile)) {
1034
1246
  fs.mkdirSync(outRoot, { recursive: true });
1035
1247
  const destFile = path.join(outRoot, name + ".tsx");
@@ -1545,9 +1757,6 @@ if (args[0] === "init") {
1545
1757
  trustless-work add escrows/escrows-by-signer/table
1546
1758
  trustless-work add escrows/escrows-by-signer/cards
1547
1759
 
1548
- --- Escrow details (optional standalone) ---
1549
- trustless-work add escrows/details
1550
-
1551
1760
  ----------------------
1552
1761
  --- SINGLE-RELEASE ---
1553
1762
  trustless-work add escrows/single-release
@@ -1557,24 +1766,6 @@ if (args[0] === "init") {
1557
1766
  - trustless-work add escrows/single-release/initialize-escrow/form
1558
1767
  - trustless-work add escrows/single-release/initialize-escrow/dialog
1559
1768
 
1560
- --- Approve milestone ---
1561
- - trustless-work add escrows/single-release/approve-milestone
1562
- - trustless-work add escrows/single-release/approve-milestone/form
1563
- - trustless-work add escrows/single-release/approve-milestone/button
1564
- - trustless-work add escrows/single-release/approve-milestone/dialog
1565
-
1566
- --- Change milestone status ---
1567
- - trustless-work add escrows/single-release/change-milestone-status
1568
- - trustless-work add escrows/single-release/change-milestone-status/form
1569
- - trustless-work add escrows/single-release/change-milestone-status/button
1570
- - trustless-work add escrows/single-release/change-milestone-status/dialog
1571
-
1572
- --- Fund escrow ---
1573
- - trustless-work add escrows/single-release/fund-escrow
1574
- - trustless-work add escrows/single-release/fund-escrow/form
1575
- - trustless-work add escrows/single-release/fund-escrow/button
1576
- - trustless-work add escrows/single-release/fund-escrow/dialog
1577
-
1578
1769
  --- Resolve dispute ---
1579
1770
  - trustless-work add escrows/single-release/resolve-dispute
1580
1771
  - trustless-work add escrows/single-release/resolve-dispute/form
@@ -1593,5 +1784,55 @@ if (args[0] === "init") {
1593
1784
  --- Dispute escrow ---
1594
1785
  - trustless-work add escrows/single-release/dispute-escrow
1595
1786
  - trustless-work add escrows/single-release/dispute-escrow/button
1787
+
1788
+ ----------------------
1789
+ --- MULTI-RELEASE ---
1790
+ trustless-work add escrows/multi-release
1791
+
1792
+ --- Initialize escrow ---
1793
+ - trustless-work add escrows/multi-release/initialize-escrow
1794
+ - trustless-work add escrows/multi-release/initialize-escrow/form
1795
+ - trustless-work add escrows/multi-release/initialize-escrow/dialog
1796
+
1797
+ --- Resolve dispute ---
1798
+ - trustless-work add escrows/multi-release/resolve-dispute
1799
+ - trustless-work add escrows/multi-release/resolve-dispute/form
1800
+ - trustless-work add escrows/multi-release/resolve-dispute/button
1801
+ - trustless-work add escrows/multi-release/resolve-dispute/dialog
1802
+
1803
+ --- Update escrow ---
1804
+ - trustless-work add escrows/multi-release/update-escrow
1805
+ - trustless-work add escrows/multi-release/update-escrow/form
1806
+ - trustless-work add escrows/multi-release/update-escrow/dialog
1807
+
1808
+ --- Release escrow ---
1809
+ - trustless-work add escrows/multi-release/release-milestone
1810
+ - trustless-work add escrows/multi-release/release-milestone/button
1811
+
1812
+ --- Dispute escrow ---
1813
+ - trustless-work add escrows/multi-release/dispute-milestone
1814
+ - trustless-work add escrows/multi-release/dispute-milestone/button
1815
+
1816
+ ----------------------
1817
+ --- SINGLE-MULTI-RELEASE -> Works with both types of escrows ---
1818
+ trustless-work add escrows/single-multi-release
1819
+
1820
+ --- Approve milestone ---
1821
+ - trustless-work add escrows/single-multi-release/approve-milestone
1822
+ - trustless-work add escrows/single-multi-release/approve-milestone/form
1823
+ - trustless-work add escrows/single-multi-release/approve-milestone/button
1824
+ - trustless-work add escrows/single-multi-release/approve-milestone/dialog
1825
+
1826
+ --- Change milestone status ---
1827
+ - trustless-work add escrows/single-multi-release/change-milestone-status
1828
+ - trustless-work add escrows/single-multi-release/change-milestone-status/form
1829
+ - trustless-work add escrows/single-multi-release/change-milestone-status/button
1830
+ - trustless-work add escrows/single-multi-release/change-milestone-status/dialog
1831
+
1832
+ --- Fund escrow ---
1833
+ - trustless-work add escrows/single-multi-release/fund-escrow
1834
+ - trustless-work add escrows/single-multi-release/fund-escrow/form
1835
+ - trustless-work add escrows/single-multi-release/fund-escrow/button
1836
+ - trustless-work add escrows/single-multi-release/fund-escrow/dialog
1596
1837
  `);
1597
1838
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustless-work/blocks",
3
- "version": "0.0.8",
3
+ "version": "1.0.0",
4
4
  "author": "Trustless Work",
5
5
  "keywords": [
6
6
  "react",
@@ -128,12 +128,15 @@ export const Actions = ({
128
128
  {/* {shouldShowEditButton && <UpdateEscrowDialog />} */}
129
129
 
130
130
  {/* Works only with single-release escrows */}
131
+ {/* Only appears if the escrow has balance */}
131
132
  {/* {shouldShowDisputeButton && <DisputeEscrowButton />} */}
132
133
 
133
134
  {/* Works only with single-release escrows */}
135
+ {/* Only appears if the escrow is disputed */}
134
136
  {/* {shouldShowResolveButton && <ResolveDisputeDialog />} */}
135
137
 
136
138
  {/* Works only with single-release escrows */}
139
+ {/* Only appears if all the milestones are approved */}
137
140
  {/* {shouldShowReleaseFundsButton && <ReleaseEscrowButton />} */}
138
141
  </div>
139
142
  )}
@@ -141,7 +141,7 @@ const MilestoneCardComponent = ({
141
141
  buttons
142
142
  .push
143
143
  // You can add the button here, using the button from the blocks. This button is conditional based on the milestone status and the user roles. Works only with multi-release escrows.
144
- // <ReleaseEscrowButton
144
+ // <ReleaseMilestoneButton
145
145
  // key={`release-${milestoneIndex}`}
146
146
  // milestoneIndex={milestoneIndex}
147
147
  // />
@@ -159,7 +159,7 @@ const MilestoneCardComponent = ({
159
159
  buttons
160
160
  .push
161
161
  // You can add the button here, using the button from the blocks. This button is conditional based on the milestone status and the user roles. Works only with multi-release escrows.
162
- // <DisputeEscrowButton
162
+ // <DisputeMilestoneButton
163
163
  // key={`dispute-${milestoneIndex}`}
164
164
  // milestoneIndex={milestoneIndex}
165
165
  // />
@@ -562,5 +562,3 @@ export const EscrowsByRoleCards = () => {
562
562
  </>
563
563
  );
564
564
  };
565
-
566
- export default React.memo(EscrowsByRoleCards);
@@ -14,13 +14,13 @@ import {
14
14
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
15
15
  import { Loader2 } from "lucide-react";
16
16
 
17
- type DisputeEscrowButtonProps = {
17
+ type DisputeMilestoneButtonProps = {
18
18
  milestoneIndex: number | string;
19
19
  };
20
20
 
21
- export const DisputeEscrowButton = ({
21
+ export const DisputeMilestoneButton = ({
22
22
  milestoneIndex,
23
- }: DisputeEscrowButtonProps) => {
23
+ }: DisputeMilestoneButtonProps) => {
24
24
  const { startDispute } = useEscrowsMutations();
25
25
  const { selectedEscrow, updateEscrow } = useEscrowContext();
26
26
  const { walletAddress } = useWalletContext();
@@ -54,7 +54,7 @@ export const DisputeEscrowButton = ({
54
54
  address: walletAddress || "",
55
55
  });
56
56
 
57
- toast.success("Escrow disputed successfully");
57
+ toast.success("Milestone disputed successfully");
58
58
 
59
59
  updateEscrow({
60
60
  ...selectedEscrow,
@@ -16,13 +16,13 @@ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogs
16
16
  import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
17
17
  import { Loader2 } from "lucide-react";
18
18
 
19
- type ReleaseEscrowButtonProps = {
19
+ type ReleaseMilestoneButtonProps = {
20
20
  milestoneIndex: number | string;
21
21
  };
22
22
 
23
- export const ReleaseEscrowButton = ({
23
+ export const ReleaseMilestoneButton = ({
24
24
  milestoneIndex,
25
- }: ReleaseEscrowButtonProps) => {
25
+ }: ReleaseMilestoneButtonProps) => {
26
26
  const { releaseFunds } = useEscrowsMutations();
27
27
  const { selectedEscrow, updateEscrow } = useEscrowContext();
28
28
  const dialogStates = useEscrowDialogs();
@@ -58,7 +58,7 @@ export const ReleaseEscrowButton = ({
58
58
  address: walletAddress || "",
59
59
  });
60
60
 
61
- toast.success("Escrow released successfully");
61
+ toast.success("Milestone released successfully");
62
62
 
63
63
  // Ensure amounts are up to date for the success dialog
64
64
  if (selectedEscrow) {
@@ -16,7 +16,7 @@ import {
16
16
  DialogTrigger,
17
17
  } from "__UI_BASE__/dialog";
18
18
  import { Loader2 } from "lucide-react";
19
- import { useApproveMilestone } from "../useApproveMilestone";
19
+ import { useApproveMilestone } from "./useApproveMilestone";
20
20
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
21
21
  import {
22
22
  Select,
@@ -8,7 +8,7 @@ import {
8
8
  FormMessage,
9
9
  } from "__UI_BASE__/form";
10
10
  import { Button } from "__UI_BASE__/button";
11
- import { useApproveMilestone } from "../useApproveMilestone";
11
+ import { useApproveMilestone } from "./useApproveMilestone";
12
12
  import { Loader2 } from "lucide-react";
13
13
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
14
14
  import {
@@ -18,7 +18,7 @@ import {
18
18
  DialogTrigger,
19
19
  } from "__UI_BASE__/dialog";
20
20
  import { Loader2 } from "lucide-react";
21
- import { useChangeMilestoneStatus } from "../useChangeMilestoneStatus";
21
+ import { useChangeMilestoneStatus } from "./useChangeMilestoneStatus";
22
22
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
23
23
  import {
24
24
  Select,
@@ -10,7 +10,7 @@ import {
10
10
  import { Input } from "__UI_BASE__/input";
11
11
  import { Textarea } from "__UI_BASE__/textarea";
12
12
  import { Button } from "__UI_BASE__/button";
13
- import { useChangeMilestoneStatus } from "../useChangeMilestoneStatus";
13
+ import { useChangeMilestoneStatus } from "./useChangeMilestoneStatus";
14
14
  import { Loader2 } from "lucide-react";
15
15
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
16
16
  import {
@@ -17,7 +17,7 @@ import {
17
17
  DialogTrigger,
18
18
  } from "__UI_BASE__/dialog";
19
19
  import { Loader2 } from "lucide-react";
20
- import { useFundEscrow } from "../useFundEscrow";
20
+ import { useFundEscrow } from "./useFundEscrow";
21
21
 
22
22
  export const FundEscrowDialog = () => {
23
23
  const { form, handleSubmit, isSubmitting } = useFundEscrow();
@@ -9,7 +9,7 @@ import {
9
9
  } from "__UI_BASE__/form";
10
10
  import { Input } from "__UI_BASE__/input";
11
11
  import { Button } from "__UI_BASE__/button";
12
- import { useFundEscrow } from "../useFundEscrow";
12
+ import { useFundEscrow } from "./useFundEscrow";
13
13
  import { Loader2 } from "lucide-react";
14
14
 
15
15
  export const FundEscrowForm = () => {