From 97d910e4f57cca9b821b9a5adb3fe6e9f5293051 Mon Sep 17 00:00:00 2001 From: Thach Nguyen Date: Tue, 1 Jul 2025 13:48:59 +0700 Subject: [PATCH 01/10] elements render skeleton --- components.json | 21 + package.json | 14 +- .../base/CompositionNodeComponent.tsx | 34 +- .../base/VisualBuilderComponent.tsx | 20 +- .../elements/ParagraphElementComponent.tsx | 34 +- .../elements/SelectionElementComponent.tsx | 46 + .../elements/SubmitElementComponent.tsx | 26 + .../elements/TextareaElementComponent.tsx | 34 + .../elements/TextboxElementComponent.tsx | 33 + src/components/ui/button.tsx | 57 + src/components/ui/input.tsx | 22 + src/components/ui/label.tsx | 24 + src/components/ui/select.tsx | 157 ++ src/components/ui/textarea.tsx | 22 + src/graphql/gql.ts | 27 +- src/graphql/graphql.ts | 1336 +++++++++++- src/lib/utils.ts | 6 + src/styles/globals.css | 73 +- tailwind.config.ts | 65 +- yarn.lock | 1839 ++++++++++++++++- 20 files changed, 3776 insertions(+), 114 deletions(-) create mode 100644 components.json create mode 100644 src/components/elements/SelectionElementComponent.tsx create mode 100644 src/components/elements/SubmitElementComponent.tsx create mode 100644 src/components/elements/TextareaElementComponent.tsx create mode 100644 src/components/elements/TextboxElementComponent.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/lib/utils.ts diff --git a/components.json b/components.json new file mode 100644 index 0000000..90e9bf9 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/package.json b/package.json index 792406a..37ec0a6 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,19 @@ }, "dependencies": { "@apollo/client": "^3.10.4", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-slot": "^1.2.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "graphql": "^16.8.1", + "lucide-react": "^0.525.0", "next": "14.2.3", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "shadcn": "^2.7.0", + "tailwind-merge": "^3.3.1", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@graphql-codegen/cli": "^5.0.2", @@ -26,5 +35,6 @@ "postcss": "^8", "tailwindcss": "^3.4.1", "typescript": "^5" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/components/base/CompositionNodeComponent.tsx b/src/components/base/CompositionNodeComponent.tsx index 96c4078..25b3d9f 100644 --- a/src/components/base/CompositionNodeComponent.tsx +++ b/src/components/base/CompositionNodeComponent.tsx @@ -1,17 +1,23 @@ import { FragmentType, useFragment } from '../../graphql/fragment-masking' import { graphql } from '@/graphql' -import ParagraphElementComponent from '../elements/ParagraphElementComponent' +import TextboxElementComponent from '../elements/TextboxElementComponent' +import TextareaElementComponent from '../elements/TextareaElementComponent' +import SelectionElementComponent from '../elements/SelectionElementComponent' +import SubmitElementComponent from '../elements/SubmitElementComponent' export const CompositionComponentNodeFragment = graphql(/* GraphQL */ ` - fragment compositionComponentNode on CompositionComponentNode { - key - component { - _metadata { - types - } - ...paragraphElement +fragment compositionComponentNode on CompositionComponentNode { + key + component { + _metadata { + types } + ...textboxElement + ...textareaElement + ...selectionElement + ...submitElement } +} `) const CompositionComponentNodeComponent = (props: { @@ -19,10 +25,18 @@ const CompositionComponentNodeComponent = (props: { }) => { const compositionComponentNode = useFragment(CompositionComponentNodeFragment, props.compositionComponentNode) const component = compositionComponentNode.component + switch (component?.__typename) { - case "ParagraphElement": - return + case "OptiFormsTextboxElement": + return + case "OptiFormsTextareaElement": + return + case "OptiFormsSelectionElement": + return + case "OptiFormsSubmitElement": + return default: + console.log(`Unknown component type: ${component?.__typename}`); return <>NotImplementedException } } diff --git a/src/components/base/VisualBuilderComponent.tsx b/src/components/base/VisualBuilderComponent.tsx index 3ed752d..a2567c9 100644 --- a/src/components/base/VisualBuilderComponent.tsx +++ b/src/components/base/VisualBuilderComponent.tsx @@ -16,14 +16,19 @@ query VisualBuilder($key: String, $version: String) { grids: nodes { ... on CompositionStructureNode { key - rows: nodes { + steps: nodes { ... on CompositionStructureNode { key - columns: nodes { + rows: nodes { ... on CompositionStructureNode { key - elements: nodes { - ...compositionComponentNode + columns: nodes { + ... on CompositionStructureNode { + key + elements: nodes { + ...compositionComponentNode + } + } } } } @@ -39,6 +44,7 @@ query VisualBuilder($key: String, $version: String) { } } } + `) interface VisualBuilderProps { @@ -78,6 +84,8 @@ const VisualBuilderComponent: FC = ({ version, contentKey }) const experience: any = experiences[experiences.length - 1]; + // console.log(experience); + if (!experience) { return null; } @@ -88,8 +96,8 @@ const VisualBuilderComponent: FC = ({ version, contentKey }) {experience?.composition?.grids?.map((grid: any) =>
- {grid.rows?.map((row: any) => -
+ {grid.steps[0].rows?.map((row: any) => +
{row.columns?.map((column: any) => (
{column.elements?.map((element: any) => diff --git a/src/components/elements/ParagraphElementComponent.tsx b/src/components/elements/ParagraphElementComponent.tsx index 720feaf..f1ee3d4 100644 --- a/src/components/elements/ParagraphElementComponent.tsx +++ b/src/components/elements/ParagraphElementComponent.tsx @@ -1,20 +1,20 @@ -import { FragmentType, useFragment } from '../../graphql/fragment-masking' -import { graphql } from '@/graphql' +// import { FragmentType, useFragment } from '../../graphql/fragment-masking' +// import { graphql } from '@/graphql' -export const ParagraphElementFragment = graphql(/* GraphQL */ ` - fragment paragraphElement on ParagraphElement { - Text { - html - } - } -`) +// export const ParagraphElementFragment = graphql(/* GraphQL */ ` +// fragment paragraphElement on ParagraphElement { +// Text { +// html +// } +// } +// `) -const ParagraphElementComponent = (props: { - paragraphElement: FragmentType -}) => { - const paragraphElement = useFragment(ParagraphElementFragment, props.paragraphElement) - // @ts-ignore - return
-} +// const ParagraphElementComponent = (props: { +// paragraphElement: FragmentType +// }) => { +// const paragraphElement = useFragment(ParagraphElementFragment, props.paragraphElement) +// // @ts-ignore +// return
+// } -export default ParagraphElementComponent \ No newline at end of file +// export default ParagraphElementComponent \ No newline at end of file diff --git a/src/components/elements/SelectionElementComponent.tsx b/src/components/elements/SelectionElementComponent.tsx new file mode 100644 index 0000000..cdf414d --- /dev/null +++ b/src/components/elements/SelectionElementComponent.tsx @@ -0,0 +1,46 @@ +import { FragmentType, useFragment } from '../../graphql/fragment-masking' +import { graphql } from '@/graphql' +import { Label } from '../ui/label' + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" + +export const SelectionElementComponentNodeFragment = graphql(/* GraphQL */ ` +fragment selectionElement on OptiFormsSelectionElement { + Label + Tooltip + AutoComplete + Validators + Options +} +`) + +const SelectionElementComponent = (props: { + selectionElement: FragmentType +}) => { + const node = useFragment(SelectionElementComponentNodeFragment, props.selectionElement) + const Options = node.Options || [] + return (<> + + + + ) +} + +export default SelectionElementComponent \ No newline at end of file diff --git a/src/components/elements/SubmitElementComponent.tsx b/src/components/elements/SubmitElementComponent.tsx new file mode 100644 index 0000000..b9addd6 --- /dev/null +++ b/src/components/elements/SubmitElementComponent.tsx @@ -0,0 +1,26 @@ +import { FragmentType, useFragment } from '../../graphql/fragment-masking' +import { graphql } from '@/graphql' +import { Input } from '../ui/input' +import { Button } from '../ui/button' + +export const SubmitElementComponentNodeFragment = graphql(/* GraphQL */ ` +fragment submitElement on OptiFormsSubmitElement { + Label + Tooltip +} +`) + +const TextboxElementComponent = (props: { + submitElement: FragmentType +}) => { + const node = useFragment(SubmitElementComponentNodeFragment, props.submitElement) + + return ( + <> +

+ + + ) +} + +export default TextboxElementComponent \ No newline at end of file diff --git a/src/components/elements/TextareaElementComponent.tsx b/src/components/elements/TextareaElementComponent.tsx new file mode 100644 index 0000000..6401f9c --- /dev/null +++ b/src/components/elements/TextareaElementComponent.tsx @@ -0,0 +1,34 @@ +import { FragmentType, useFragment } from '../../graphql/fragment-masking' +import { graphql } from '@/graphql' +import { Textarea } from '../ui/textarea' +import { Label } from '../ui/label' + +export const TextareaComponentNodeFragment = graphql(/* GraphQL */ ` +fragment textareaElement on OptiFormsTextareaElement { + Label + Tooltip + Placeholder + AutoComplete + PredefinedValue + Validators +} +`) + +const TextareaElementComponent = (props: { + textareElement: FragmentType +}) => { + const node = useFragment(TextareaComponentNodeFragment, props.textareElement) + + return ( + <> +
+
+
+