-
Notifications
You must be signed in to change notification settings - Fork 11
Feature/builder functionalities #162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
JMauclair
wants to merge
44
commits into
main
Choose a base branch
from
feature/builder_functionalities
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
ba1784e
refactor: enhance TreeNode and TreeView to support rootExpand prop fo…
JMauclair 9c59e6e
Merge branch 'main' of https://github.com/Klickbee/klickbee-cms into …
JMauclair ed701db
chore: add generated CSS file to .gitignore
JMauclair ebe81e0
feat: implement CSS generation functionality for builder components
JMauclair e1fe251
feat: add event handlers and className prop to builder components for…
JMauclair 5a60791
feat: enhance ComponentRenderer to support click and drag event handl…
JMauclair 25e2f96
feat: add breakpoint style handling functions for responsive componen…
JMauclair 086a2f3
feat: integrate BreakpointProvider and enhance breakpoint handling in…
JMauclair bde42b0
feat: create Zustand store for managing active breakpoints in builder…
JMauclair cf60cae
feat: add ComponentName type to currentComponent store for improved t…
JMauclair 9e1646e
feat: add BreakpointStyleProps type for improved breakpoint styling m…
JMauclair de8589f
feat: add name property to various component definitions for improved…
JMauclair 68fcec9
feat: add new components support to enhance the component library
JMauclair f873127
feat: enhance BuilderComponent with name property and support for bre…
JMauclair 76b9e4b
feat: update useFooterEditor to support BreakpointStyleProps for impr…
JMauclair 7ac6e29
feat: update useHeaderEditor to utilize BreakpointStyleProps for enha…
JMauclair 211d1c0
feat: refactor PageRenderer to improve component structure and add ID…
JMauclair a4ac930
feat: update .gitignore to ignore all generated files in the app dire…
JMauclair bf2fbc6
feat: add generated.css and update layout to import it
JMauclair 0affdf4
refactor: replace trash and plus button by dropdown for UX purpose
JMauclair 4477b68
fix: missing component name property in DnD of component tab to viewport
JMauclair d007426
feat: enhance drag-and-drop functionality with DragOverlay for better UX
JMauclair 6915c26
feat: add generated.css to .gitignore to prevent tracking of generate…
JMauclair 90409c3
feat: import generated.css in globals.css for styling enhancements
JMauclair 5fd97fd
feat: simplify style prop in Section component by removing unused pro…
JMauclair ca3e03a
feat: enhance mapStylePropsToCss to support breakpoint styles and imp…
JMauclair 026817b
feat: enhance CSS generation to support breakpoint-specific styles in…
JMauclair cf479b2
feat: update default breakpoint width to 1440 in BreakpointContext
JMauclair e2f636f
feat: integrate mapStylePropsToCss for dynamic styling in SubmitButton
JMauclair e60431a
feat: integrate useCssGeneration in page header, footer, and actions …
JMauclair 67541a2
feat: update CSS import for new page structure in generated.css
JMauclair dfdc746
feat: refactor component rendering logic to integrate active breakpoi…
JMauclair f3f7063
feat: integrate active breakpoint handling in Builder and Layers comp…
JMauclair c630718
feat: optimize import statements and clean up unused code in Layers c…
JMauclair d92bbbe
feat: wrap img element in a div for improved styling and layout control
JMauclair aa62319
feat: enhance component selection styling with updated border thickness
JMauclair 7768ad8
feat: reduce border thickness for selected and drop target states in …
JMauclair 4a74b37
feat: enhance size and spacing handling with default values and impro…
JMauclair eb00bff
feat: update media query logic to use max-width for breakpoint handling
JMauclair a0aa89f
feat: add CSS generation for header and footer components in usePageH…
JMauclair 5712145
feat: implement PagePresenter component for dynamic page rendering wi…
JMauclair 35278d7
feat: extend sizeUnits to include viewport width and height units
JMauclair 6475a73
feat: enhance to support custom unit handling and add TextInput compo…
JMauclair c0c6a4d
feat: refine CSS generation for builder container by removing width p…
JMauclair File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
src/app/admin/[adminKey]/builder/present/[id]/[breakpoint]/page.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| "use client"; | ||
|
|
||
| import React, { Usable } from "react"; | ||
| import { BreakpointProvider } from "@/feature/builder/contexts/BreakpointContext"; | ||
| import { usePageFooterByPage } from "@/feature/page/_footer/queries/usePageFooter"; | ||
| import { usePageHeaderByPage } from "@/feature/page/_header/queries/usePageHeader"; | ||
| import { usePageById } from "@/feature/page/queries/usePageById"; | ||
| import { PageLight } from "@/feature/page/types/page"; | ||
| import { PageFooter, PageHeader } from "@/generated/prisma"; | ||
| import { PageRenderer } from "@/public/renderer/PageRenderer"; | ||
|
|
||
| export default function PagePresenter({ | ||
| params, | ||
| }: { | ||
| params: { id: string; breakpoint: string }; | ||
| }) { | ||
| const { id: idParam, breakpoint: bpParam } = React.use( | ||
| params as unknown as Usable<{ id: string; breakpoint: string }>, | ||
| ); | ||
|
|
||
| // Support builder opening with "new" when no page exists yet | ||
| if (idParam === "new") { | ||
| const bpNum = parseInt(bpParam || "0", 10) || 0; | ||
| return ( | ||
| <div style={{ padding: 16 }}> | ||
| <h2>Preview (new page)</h2> | ||
| <div | ||
| style={{ | ||
| width: bpNum ? `${bpNum}px` : "100%", | ||
| maxWidth: bpNum ? `${bpNum}px` : "100%", | ||
| margin: "0 auto", | ||
| border: "1px solid #e5e7eb", | ||
| background: "white", | ||
| minHeight: 400, | ||
| }} | ||
| /> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| const pageId = Number(idParam); | ||
| const breakpoint = Number(bpParam) || 0; | ||
| const bpName = breakpoint ? `bp-${breakpoint}` : "default"; | ||
|
|
||
| const { | ||
| error: pageError, | ||
| data: page, | ||
| isLoading: pageLoading, | ||
| } = usePageById(pageId); | ||
| const { data: pageHeader } = usePageHeaderByPage( | ||
| pageId > 0 ? pageId : undefined, | ||
| ); | ||
| const { data: pageFooter } = usePageFooterByPage( | ||
| pageId > 0 ? pageId : undefined, | ||
| ); | ||
|
|
||
| if (pageLoading) { | ||
| return <div style={{ padding: 16 }}>Loading preview…</div>; | ||
| } | ||
|
|
||
| // Handle possible auth error response shape returned by server helpers | ||
| if (!page) { | ||
| return <div style={{ padding: 16 }}>Unable to load page.</div>; | ||
| } | ||
| if (pageError) { | ||
| return ( | ||
| <div style={{ padding: 16 }}> | ||
| Error loading page: {pageError.message} | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| // page is expected to be a Page object at this point | ||
| const content = (page as PageLight).content; | ||
|
|
||
| return ( | ||
| <div style={{ padding: 16 }}> | ||
| <h1 | ||
| style={{ | ||
| marginBottom: 8, | ||
| padding: 24, | ||
| color: "#fff", | ||
| textAlign: "center", | ||
| backgroundColor: "black", | ||
| }} | ||
| > | ||
| Preview - Page {pageId} - {breakpoint}px | ||
| </h1> | ||
| <div | ||
| style={{ | ||
| width: breakpoint ? `${breakpoint}px` : "100%", | ||
| maxWidth: breakpoint ? `${breakpoint}px` : "100%", | ||
| margin: "0 auto", | ||
| background: "white", | ||
| border: "1px solid #e5e7eb", | ||
| minHeight: 200, | ||
| overflow: "hidden", | ||
| }} | ||
| > | ||
| <BreakpointProvider | ||
| value={{ name: bpName, width: breakpoint || 1440 }} | ||
| > | ||
| <PageRenderer | ||
| content={content} | ||
| footerContent={(pageFooter as PageHeader)?.content} | ||
| headerContent={(pageHeader as PageFooter)?.content} | ||
| /> | ||
| </BreakpointProvider> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| "use server"; | ||
|
|
||
| import { promises as fs } from "fs"; | ||
| import path from "path"; | ||
|
|
||
| export async function generateCssAction({ | ||
| css, | ||
| pageId, | ||
| fileName, | ||
| }: { | ||
| css: string; | ||
| pageId: number | string; | ||
| fileName: string; // sanitized filename computed client-side | ||
| }): Promise<{ ok: boolean; file?: string; error?: string }> { | ||
| if (!css || !fileName || (!pageId && pageId !== 0)) { | ||
| return { ok: false, error: "Missing css, fileName, or pageId" }; | ||
| } | ||
|
|
||
| try { | ||
| const cwd = process.cwd(); | ||
| const appDir = path.join(cwd, "src", "app"); | ||
| const generatedDir = path.join(appDir, "generated"); | ||
| const generated = path.join(appDir, "generated.css"); | ||
| const targetCssPath = path.join(generatedDir, fileName); | ||
|
|
||
| // Ensure generated directory exists | ||
| await fs.mkdir(generatedDir, { recursive: true }); | ||
|
|
||
| // Write or replace the per-page CSS file | ||
| await fs.writeFile(targetCssPath, css, "utf8"); | ||
|
|
||
| // Update globals.css to import this CSS | ||
| let globals = ""; | ||
| try { | ||
| globals = await fs.readFile(generated, "utf8"); | ||
| } catch (_e) { | ||
| // If globals.css doesn't exist, create it | ||
| globals = ""; | ||
| } | ||
|
|
||
| const pageIdStr = String(pageId); | ||
| const importRegex = new RegExp( | ||
| String.raw`^\s*@import\s+["']\.\/generated\/page-${pageIdStr}-[^"']+["'];\s*$`, | ||
| "gm", | ||
| ); | ||
|
|
||
| // Remove any existing imports for this page id | ||
| globals = globals.replace(importRegex, "").trimEnd() + "\n"; | ||
|
|
||
| const newImport = `@import "./generated/${fileName}";`; | ||
|
|
||
| if (!globals.includes(newImport)) { | ||
| // Append the new import near the top, after existing imports if any | ||
| const lines = globals.split(/\r?\n/); | ||
| let insertIndex = 0; | ||
| while ( | ||
| insertIndex < lines.length && | ||
| lines[insertIndex].trim().startsWith("@import") | ||
| ) { | ||
| insertIndex++; | ||
| } | ||
| lines.splice(insertIndex, 0, newImport); | ||
| globals = lines.join("\n"); | ||
| await fs.writeFile(generated, globals, "utf8"); | ||
| } | ||
|
|
||
| return { | ||
| ok: true, | ||
| file: `src/app/generated/${fileName}`, | ||
| }; | ||
| } catch (error) { | ||
| return { | ||
| ok: false, | ||
| error: (error as { message: string })?.message ?? String(error), | ||
| }; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
Copilot Autofix
AI 6 months ago
To mitigate this vulnerability, server-side validation of
fileNamemust be performed before constructing and using any file path based on it. The best way to fix this is to ensure that the generated path (after joininggeneratedDirandfileNameand normalizing) is still insidegeneratedDir, preventing path traversal. Steps:path.resolveto combinegeneratedDirandfileName, producing an absolute, normalized path.generatedDir(for example, usingstartsWith).fileNameis a "safe" filename (e.g., only alphanumerics, dashes, underscore, .css extension)—but the path containment check is more general and robust.Modify only the section between lines 24 and 30 of
src/feature/builder/actions/cssActions.ts.No additional dependencies are needed; all can be done with Node.js built-in
path.