|
| 1 | +import prettier from "prettier"; |
| 2 | +import comment from "../../comment"; |
| 3 | +import type { ColumnResponse, TablesResponse } from "../../pgMeta/fetchTables"; |
| 4 | +import type { File, HookFile } from "../../types"; |
| 5 | +import { parseNameFormats } from "../../utils"; |
| 6 | + |
| 7 | +const mapColumns = ( |
| 8 | + columns?: ColumnResponse[] |
| 9 | +): { fields: string; inputs: string } => { |
| 10 | + if (!columns) { |
| 11 | + return { fields: "", inputs: "" }; |
| 12 | + } |
| 13 | + |
| 14 | + const filteredColumns = columns.filter( |
| 15 | + (column) => column.isIdentity || column.isUpdatable |
| 16 | + ); |
| 17 | + const fields = filteredColumns |
| 18 | + .filter((column) => !column.isIdentity) |
| 19 | + .map((column) => `"${column.name}"`) |
| 20 | + .join(","); |
| 21 | + const inputs = filteredColumns |
| 22 | + .map((column, index) => { |
| 23 | + const label = column.isIdentity ? `${column.name}*` : column.name; |
| 24 | + return ` |
| 25 | + <div className="flex items-center"> |
| 26 | + <label |
| 27 | + htmlFor="${column.name}" |
| 28 | + style={{ |
| 29 | + flexBasis: "200px", |
| 30 | + marginRight: "10px" |
| 31 | + }} |
| 32 | + > |
| 33 | + ${label} |
| 34 | + </label> |
| 35 | + <label |
| 36 | + htmlFor="${column.name}" |
| 37 | + style={{ |
| 38 | + flexBasis: "200px", |
| 39 | + }} |
| 40 | + > |
| 41 | + ${column.dataType} |
| 42 | + </label> |
| 43 | + <input |
| 44 | + type="text" |
| 45 | + id="${column.name}" |
| 46 | + style={{ |
| 47 | + ${index > 0 ? 'marginTop: "10px",' : ""} |
| 48 | + background: "#000", |
| 49 | + color: "#fff", |
| 50 | + border: "1px solid #34383A", |
| 51 | + marginLeft: "10px", |
| 52 | + flex: "1", |
| 53 | + borderRadius: "0.375rem", |
| 54 | + padding: "4px 16px", |
| 55 | + }} |
| 56 | + /> |
| 57 | + </div> |
| 58 | + `; |
| 59 | + }) |
| 60 | + .join(" "); |
| 61 | + |
| 62 | + return { fields, inputs }; |
| 63 | +}; |
| 64 | + |
| 65 | +export const mapHookFileToUpdateComponent = async ( |
| 66 | + hookFile: HookFile, |
| 67 | + tables: TablesResponse |
| 68 | +): Promise<File> => { |
| 69 | + const { |
| 70 | + entityName, |
| 71 | + file: { fileName }, |
| 72 | + } = hookFile; |
| 73 | + |
| 74 | + const table = tables.find((table) => table.name === entityName); |
| 75 | + const componentName = `${fileName.replace("use", "")}`; |
| 76 | + const { pascalCase } = parseNameFormats(componentName); |
| 77 | + |
| 78 | + const { fields, inputs } = mapColumns(table?.columns); |
| 79 | + |
| 80 | + const content = ` |
| 81 | + ${comment} |
| 82 | +
|
| 83 | + "use client"; |
| 84 | + |
| 85 | + import { FormEventHandler, MouseEventHandler, useState } from "react"; |
| 86 | + import type { Row, Update${pascalCase} } from "../../hooks/${fileName}"; |
| 87 | +
|
| 88 | + const fields: Array<keyof Update${pascalCase}> = [${fields}] |
| 89 | + |
| 90 | + export default function Update${componentName}({ |
| 91 | + onUpdate, |
| 92 | + onFetch |
| 93 | + }: { |
| 94 | + onUpdate: (id: Row["id"], updatedRow: Update${pascalCase}) => Promise<Row | undefined>, |
| 95 | + onFetch: () => Promise<void> |
| 96 | + }) { |
| 97 | + const [message, setMessage] = useState<string>(); |
| 98 | + |
| 99 | + const handleSubmit: FormEventHandler = (event) => { |
| 100 | + event.preventDefault(); |
| 101 | + const target = event.target as typeof event.target & Update${pascalCase}; |
| 102 | + const id = (target["id"] as any)?.value; |
| 103 | + const updatedRow = fields |
| 104 | + .map((field) => ({ field, value: (target[field] as any)?.value })) |
| 105 | + .reduce((newRow, { field,value }) => { |
| 106 | + if (value.trim() !== "") { |
| 107 | + newRow[field] = value; |
| 108 | + } |
| 109 | + return newRow; |
| 110 | + }, {} as Record<keyof Update${pascalCase}, any>); |
| 111 | + onUpdate(id, updatedRow) |
| 112 | + .then((task) => { |
| 113 | + if (task) { |
| 114 | + setMessage("row with id " + task.id + " updated!"); |
| 115 | + onFetch(); |
| 116 | + } else { |
| 117 | + setMessage("failed to update row!"); |
| 118 | + } |
| 119 | + }) |
| 120 | + .catch((error) => { |
| 121 | + if (error.message) { |
| 122 | + setMessage(error.message); |
| 123 | + } else { |
| 124 | + setMessage("failed to update row!"); |
| 125 | + } |
| 126 | + }); |
| 127 | + }; |
| 128 | + |
| 129 | + const handleClick: MouseEventHandler<HTMLButtonElement> = () => { |
| 130 | + setMessage(undefined); |
| 131 | + } |
| 132 | + |
| 133 | + if (message) { |
| 134 | + return <div style={{ display: "flex", flexDirection: "column", height: "98px", justifyContent: "end" }}> |
| 135 | + {message} |
| 136 | + <button |
| 137 | + style={{ |
| 138 | + background: "#fff", |
| 139 | + color: "#000", |
| 140 | + marginTop: "20px", |
| 141 | + padding: "8px 10px", |
| 142 | + width: "200px", |
| 143 | + borderRadius: "0.375rem", |
| 144 | + display: "flex", |
| 145 | + alignItems: "center", |
| 146 | + justifyContent: "center", |
| 147 | + }} |
| 148 | + onClick={handleClick} |
| 149 | + > |
| 150 | + Go Back |
| 151 | + </button> |
| 152 | + </div> |
| 153 | + } |
| 154 | + |
| 155 | + return ( |
| 156 | + <div |
| 157 | + style={{ |
| 158 | + paddingTop: "20px", |
| 159 | + }} |
| 160 | + > |
| 161 | + <form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column" }}> |
| 162 | + ${inputs} |
| 163 | + <button |
| 164 | + type="submit" |
| 165 | + style={{ |
| 166 | + background: "#fff", |
| 167 | + color: "#000", |
| 168 | + marginTop: "10px", |
| 169 | + padding: "8px 10px", |
| 170 | + width: "200px", |
| 171 | + borderRadius: "0.375rem", |
| 172 | + display: "flex", |
| 173 | + alignItems: "center", |
| 174 | + justifyContent: "center", |
| 175 | + }} |
| 176 | + > |
| 177 | + <div style={{ marginRight: "10px" }}>Run PUT</div> |
| 178 | + <svg |
| 179 | + fill="none" |
| 180 | + viewBox="0 0 24 24" |
| 181 | + strokeWidth={1.5} |
| 182 | + stroke="currentColor" |
| 183 | + style={{ |
| 184 | + height: "20px", |
| 185 | + }} |
| 186 | + > |
| 187 | + <path |
| 188 | + strokeLinecap="round" |
| 189 | + strokeLinejoin="round" |
| 190 | + d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z" |
| 191 | + /> |
| 192 | + </svg> |
| 193 | + </button> |
| 194 | + </form> |
| 195 | + </div> |
| 196 | + ); |
| 197 | + } |
| 198 | + `; |
| 199 | + |
| 200 | + const formattedContent = await prettier.format(content, { |
| 201 | + parser: "typescript", |
| 202 | + }); |
| 203 | + |
| 204 | + return { |
| 205 | + fileName: `Update${componentName}.tsx`, |
| 206 | + content: formattedContent, |
| 207 | + }; |
| 208 | +}; |
0 commit comments