This commit is contained in:
2024-01-10 17:35:18 +05:00
parent 72f68c3325
commit 42c7f15cdd
13 changed files with 124 additions and 97 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
VITE_API_URL=https://graff.estate
# VITE_API_URL=http://192.168.1.171:3003
VITE_API_URL=https://graff.estate/api
# VITE_API_URL=http://localhost:3003
+1 -1
View File
@@ -7,7 +7,7 @@ function ModalContainer() {
return (
<div
// onClick={() => setModal(null)}
className={`fixed p-8 top-0 left-0 w-full h-full flex justify-center items-center bg-black bg-opacity-80 overflow-auto transition-opacity`}
className={`fixed p-8 top-0 left-0 z-10 w-full h-full flex justify-center items-center bg-black bg-opacity-80 overflow-auto transition-opacity`}
>
<div onClick={(e) => e.stopPropagation()} className="cursor-default">
{modal}
+1 -1
View File
@@ -28,7 +28,7 @@ function ProjectCard({
<div
className="group-hover:scale-110 transition-transform duration-500 absolute top-0 left-0 w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url(${import.meta.env.VITE_API_URL}/api/upload/${image})`,
backgroundImage: `url(${import.meta.env.VITE_API_URL}/upload/${image})`,
}}
></div>
<div className="absolute top-0 left-0 w-full h-full bg-gradient-card"></div>
@@ -44,7 +44,7 @@ function CreateProjectModal() {
setProject((prev) => ({
...prev,
image: `${import.meta.env.VITE_API_URL}/api/${file}`,
image: file,
}));
} catch (error) {
if (error instanceof Error) {
@@ -143,7 +143,9 @@ function CreateProjectModal() {
className="absolute opacity-0"
onChange={handleChangeFile}
/>
<p>{file ? file.name : "Выберите изображение"}</p>
<p className="truncate">
{file ? file.name : "Выберите изображение"}
</p>
{previewFile && <img src={previewFile} alt="" />}
</label>
@@ -42,13 +42,13 @@ function EditProjectModal({ projectId }: EditProjectModalProps) {
formData.append("file", file);
try {
const { file: url }: { file: string } = await api
const { file }: { file: string } = await api
.post("upload", { body: formData })
.json();
setProject((prev) => ({
...prev,
image: `${import.meta.env.VITE_API_URL}/api/${url}`,
image: file,
}));
} catch (error) {
if (error instanceof Error) {
@@ -161,12 +161,17 @@ function EditProjectModal({ projectId }: EditProjectModalProps) {
className="absolute opacity-0"
onChange={handleChangeFile}
/>
<p>{file ? file.name : "Выберите изображение"}</p>
<p className="truncate">{file ? file.name : "Выберите изображение"}</p>
{previewFile ? (
<img src={previewFile} alt="" />
) : (
project.image && <img src={project.image} alt="" />
project.image && (
<img
src={`${import.meta.env.VITE_API_URL}/upload/${project.image}`}
alt=""
/>
)
)}
</label>
+6 -6
View File
@@ -1,18 +1,18 @@
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./App.tsx";
import "./index.css";
// import ProjectsPage from "./pages/ProjectsPage.tsx";
import App from "./App.tsx";
import ProjectsPage from "./pages/ProjectsPage.tsx";
const router = createBrowserRouter([
{
path: "/",
element: <App />,
},
// {
// path: "/projects",
// element: <ProjectsPage />,
// },
{
path: "/projects",
element: <ProjectsPage />,
},
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
+42 -10
View File
@@ -34,12 +34,12 @@ function ProjectsPage() {
}, []);
return (
<div className="min-h-screen 2xl:px-10 xl:px-8 sm:px-6 px-4 overflow-x-clip">
<div className="relative conatiner mx-auto 2xl:max-w-screen-2xl">
<div className="py-8 flex flex-col gap-8">
<Button onClick={handleClickCreateProject}>
Добавить проект
</Button>
<div className="min-h-screen">
<div className="fixed top-0 left-0 z-10 p-4 bg-[#14161F] border-b border-white border-opacity-10 w-full shadow-2xl">
<Button onClick={handleClickCreateProject}>Добавить проект</Button>
</div>
<div className="conatiner mx-auto 2xl:px-10 xl:px-8 sm:px-6 px-4 2xl:max-w-screen-2xl mt-20">
<div className="relative py-8 flex flex-col gap-8">
<div className="grid grid-cols-3 gap-4">
{projects.map((project, index) => (
<div key={index} className="relative">
@@ -49,17 +49,49 @@ function ProjectsPage() {
onClick={() =>
setModal(<EditProjectModal projectId={project.id!} />)
}
className="px-3 py-2 border bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity"
className="group relative p-2 bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity rounded-full"
>
Редактировать
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
/>
</svg>
<span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
Редактировать
</span>
</button>
<button
onClick={() =>
setModal(<DeleteProjectModal projectId={project.id!} />)
}
className="px-3 py-2 border bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity"
className="group relative p-2 bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity rounded-full"
>
Удалить
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>
<span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
Удалить
</span>
</button>
</div>
</div>
-11
View File
@@ -1,11 +0,0 @@
function TestPage() {
return (
<video
src="https://graff.estate/videos/features/nks_infra.mp4"
preload="metadata"
className="video-loader"
></video>
);
}
export default TestPage;
+1 -1
View File
@@ -1,7 +1,7 @@
import ky from "ky";
const api = ky.extend({
prefixUrl: `${import.meta.env.VITE_API_URL}/api`,
prefixUrl: import.meta.env.VITE_API_URL,
});
export default api;
-3
View File
@@ -1,3 +0,0 @@
export default function randomNumber(min: number, max: number) {
return Math.floor(Math.random() * (max - min) + min);
}
-1
View File
@@ -4,7 +4,6 @@ import react from "@vitejs/plugin-react-swc";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
// const env = loadEnv(mode, process.cwd());
return {
plugins: [react()],
+2
View File
@@ -4,6 +4,7 @@ import express, { json } from "express";
import cors from "cors";
import mailRoute from "./routes/mail";
import projectRoute from "./routes/projects";
import uploadRoute from "./routes/upload";
connectDB();
@@ -15,6 +16,7 @@ app.use(cors({ origin: "*" }));
app.use("/mail", mailRoute);
app.use("/projects", projectRoute);
app.use("/upload", uploadRoute);
app.listen(port, () => {
console.log(`Server is listening on port ${port}`);
+56 -55
View File
@@ -1,77 +1,78 @@
import { Router } from "express";
// import multer, { memoryStorage } from "multer";
// import sharp from "sharp";
// import fs from "fs";
import multer, { memoryStorage } from "multer";
import sharp from "sharp";
import fs from "fs";
import path from "path";
// import { v4 as uuidv4 } from "uuid";
import { v4 as uuidv4 } from "uuid";
const router = Router();
// const upload = multer({
// dest: "uploads/",
// storage: memoryStorage(),
// fileFilter: (_req, file, cb) => {
// if (file.mimetype.split("/")[0] === "image") {
// cb(null, true);
// } else {
// cb(new Error("Only images are allowed!"));
// }
// },
// });
const upload = multer({
dest: "uploads/",
storage: memoryStorage(),
fileFilter: (_req, file, cb) => {
if (file.mimetype.split("/")[0] === "image") {
cb(null, true);
} else {
cb(new Error("Only images are allowed!"));
}
},
});
router.get("/", (_req, res) => {
res.json({ ok: 1 });
});
// router.get("/:filename", (req, res) => {
// res.sendFile(`${path.resolve("uploads")}/${req.params.filename}`);
// });
router.get("/:filename", (req, res) => {
res.sendFile(`${path.resolve("uploads")}/${req.params.filename}`);
});
// router.get("/:file", (req, res) => {
// const fileName = req.params.file;
router.get("/:file", (req, res) => {
const fileName = req.params.file;
// console.log(fileName);
console.log(fileName);
// try {
// const readStream = fs.createReadStream(
// `${path.join(__dirname, "uploads")}/${fileName}`
// );
try {
const readStream = fs.createReadStream(
`${path.join(__dirname, "uploads")}/${fileName}`
);
// console.log(readStream);
console.log(readStream);
// readStream.pipe(res);
// } catch (error) {
// if (error instanceof Error) {
// return res.json({ error: error.message });
// }
// }
// });
readStream.pipe(res);
} catch (error) {
if (error instanceof Error) {
return res.json({ error: error.message });
}
}
});
// router.post("/", upload.single("file"), async (req, res) => {
// if (!req.file) {
// return res.json({ error: "req.file" });
// }
router.post("/", upload.single("file"), async (req, res) => {
if (!req.file) {
return res.json({ error: "req.file" });
}
// try {
// const filename = `${uuidv4()}.jpg`;
try {
const filename = `${uuidv4()}.jpg`;
// await sharp(req.file.buffer)
// .resize({
// width: 728,
// height: 728,
// fit: "inside",
// withoutEnlargement: true,
// })
// .jpeg({ quality: 90 })
// .toFile(`uploads/${filename}`);
await sharp(req.file.buffer)
.resize({
width: 728,
height: 728,
fit: "inside",
withoutEnlargement: true,
})
.jpeg({ quality: 90 })
.toFile(`uploads/${filename}`);
// return res.json({ file: `/upload/${filename}` });
// } catch (error) {
// if (error instanceof Error) {
// return res.json({ error: error.message });
// }
// }
// });
return res.json({ file: filename });
} catch (error) {
if (error instanceof Error) {
return res.json({ error: error.message });
}
}
});
const uploadRoute = router;
export default uploadRoute;