Skip to content

Commit

Permalink
resolved merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
MeetDOD committed Jul 12, 2024
2 parents 6481cc5 + 9f43667 commit ef95830
Show file tree
Hide file tree
Showing 31 changed files with 1,013 additions and 255 deletions.
2 changes: 1 addition & 1 deletion admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="./src/assets/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
Expand Down
51 changes: 50 additions & 1 deletion admin/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@
},
"dependencies": {
"axios": "^1.7.2",
"dompurify": "^3.1.6",
"chart.js": "^4.4.3",
"react": "^18.3.1",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.3.1",
"react-hot-toast": "^2.4.1",
"react-icons": "^5.2.1",
"react-router-dom": "^6.24.0",
"react-switch": "^7.0.0",
"recoil": "^0.7.7"
},
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
Expand Down
9 changes: 9 additions & 0 deletions admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Profile from "./pages/Profile";
import Users from "./pages/Users";
import Posts from "./pages/Posts";
import { Toaster } from "react-hot-toast";
import UpdatePost from "./components/UpdatePost";
import Graphs from "./pages/Graphs";
// import axios from "axios";
// axios.defaults.baseURL = "http://localhost:3001/";
Expand Down Expand Up @@ -60,6 +61,14 @@ function App() {
</AuthenticatedRoute>
}
/>
<Route
path="/admin/update-post/:postId"
element={
<AuthenticatedRoute>
<UpdatePost />
</AuthenticatedRoute>
}
/>
<Route
path="/admin/statistics"
element={
Expand Down
95 changes: 95 additions & 0 deletions admin/src/components/CodeEditorAndPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { useState } from 'react';
import PostPreview from "../components/PostPreview";
import Switch from "react-switch";
import toast from "react-hot-toast";

type Props = {
codeSnippet: string;
jsCodeSnippet: string;
setCodeSnippet: (codeSnippet: string) => void;
setJsCodeSnippet: (jsCodeSnippet: string) => void;
};

function CodeEditorAndPreview({
codeSnippet,
jsCodeSnippet,
setCodeSnippet,
setJsCodeSnippet
}: Props) {
const [isPreviewMode, setIsPreviewMode] = useState(false);

const handleToggle = () => {
setIsPreviewMode(!isPreviewMode);
};

const handleTextAreaClick = () => {
if (isPreviewMode) {
toast('Please disable preview mode to edit the code snippet', {
icon: '🚫',
});
}
};


return (
<div>
<div>
<label htmlFor="codeSnippet" className="block text-gray-700 text-sm font-semibold">
HTML
</label>
<textarea
id="codeSnippet"
value={codeSnippet}
onChange={(e) => setCodeSnippet(e.target.value)}
className="mt-1 shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
readOnly={isPreviewMode}
onClick={handleTextAreaClick}
rows={5}
></textarea>
</div>
<div>
<label htmlFor="jsCodeSnippet" className="block text-gray-700 text-sm font-semibold">
Javascript
</label>
<textarea
id="jsCodeSnippet"
value={jsCodeSnippet}
onChange={(e) => setJsCodeSnippet(e.target.value)}
className="mt-1 shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
readOnly={isPreviewMode}
onClick={handleTextAreaClick}
rows={4}
></textarea>
</div>
<div className="flex items-center mt-2 mb-2">
<label className="flex items-center">
<Switch
onChange={handleToggle}
checked={isPreviewMode}
offColor="#888"
onColor="#080"
uncheckedIcon={false}
checkedIcon={false}
disabled={codeSnippet == ""}
height={16}
width={32}
className="mr-2"
/>
<span className="text-gray-700 text-sm font-semibold">
{isPreviewMode ? 'Edit Code' : 'Preview Component'}
</span>
</label>
</div>
{isPreviewMode ? (
<PostPreview
jsCodeSnippet={jsCodeSnippet}
codeSnippet={codeSnippet}
/>
) : null}

</div>
);

}

export default CodeEditorAndPreview;
88 changes: 88 additions & 0 deletions admin/src/components/PostPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useEffect, useRef, useState } from "react";
import DOMPurify from "dompurify"

function PostPreview({
codeSnippet,
jsCodeSnippet,
}: {
codeSnippet: string;
jsCodeSnippet: string;
}) {
const ref = useRef<HTMLIFrameElement>(null);
const [height, setHeight] = useState("100px");

useEffect(() => {
const handleResize = (event: MessageEvent) => {
if (event.data.type === "setHeight") {
setHeight(event.data.height + "px");
}
};

window.addEventListener("message", handleResize);
return () => {
window.removeEventListener("message", handleResize);
};
}, []);

DOMPurify.addHook("uponSanitizeElement", (node, data) => {
if (data.tagName === "img" || data.tagName === "div") {
const src = node.getAttribute("src");
const style = node.getAttribute("style");
if (src && src.startsWith("http")) {
node.setAttribute("src", src);
}
if (style && style.includes("url(")) {
node.setAttribute("style", style);
}
}
});

const sanitizedSnippet = DOMPurify.sanitize(codeSnippet || "", {
ADD_ATTR: ["style", "background"],
});

return (
<div className="p-4 text-[#000435] bg-white dark:text-white dark:bg-[#fff] z-0 overflow-hidden rounded border-2 border-sky-400">
<iframe
ref={ref}
className="w-full border-0"
srcDoc={getPreviewTemplate(sanitizedSnippet, jsCodeSnippet)}
title="Preview"
sandbox="allow-scripts allow-modals"
style={{ minHeight: height, maxWidth: "100%" }}
/>
</div>
);
}

function getPreviewTemplate(sanitizedSnippet: string, jsCodeSnippet: string) {
return `
<!DOCTYPE html>
<html class='flex w-full h-full'>
<head>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
<script>
document.addEventListener('DOMContentLoaded', function() {
const setHeight = () => {
const height = document.body.scrollHeight;
window.parent.postMessage({ type: 'setHeight', height }, '*');
};
setHeight();
const observer = new MutationObserver(setHeight);
observer.observe(document.body, { childList: true, subtree: true });
window.addEventListener('beforeunload', () => observer.disconnect());
});
</script>
</head>
<body class='w-full h-full flex items-center justify-center min-w-full min-h-full'>
<div class='w-full h-full p-6'>${sanitizedSnippet}</div>
${jsCodeSnippet ? `<script defer>${jsCodeSnippet}</script>` : ""}
</body>
</html>
`;
}

export default PostPreview;
4 changes: 3 additions & 1 deletion admin/src/components/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleS
return (
<div className={`lg:rounded-xl rounded-none fixed z-30 inset-y-0 left-0 w-72 lg:m-5 transition-transform transform ${sidebarOpen ? "translate-x-0" : "-translate-x-full"} lg:translate-x-0 bg-[#000435] text-white`}>
<div className="flex items-center justify-between mr-6 mt-3 h-16 px-4">
<a href='/app' className="text-xl flex font-bold"><img src={logo} className="h-8 mx-3" alt="Styleshare Logo" /> Style Share</a>
<Link to='/admin' className="text-xl flex font-bold">
<img src={logo} className="h-8 mx-3" alt="Styleshare Logo" />
Style Share</Link>
<button onClick={toggleSidebar} className="lg:hidden text-2xl">
<FaTimes />
</button>
Expand Down
Loading

0 comments on commit ef95830

Please sign in to comment.