first commit

This commit is contained in:
2026-04-16 18:37:05 +02:00
parent c1289212b7
commit 586cd69b68
11 changed files with 2994 additions and 202 deletions

2748
deno.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,9 @@
"dependencies": { "dependencies": {
"next": "16.2.3", "next": "16.2.3",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4" "react-dom": "19.2.4",
"react-drag-drop-files": "^3.1.0",
"sass": "^1.99.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^9", "eslint": "^9",

View File

@@ -0,0 +1,12 @@
import { useEffect } from "react"
export default function Preview({daySelected}){
useEffect(()=>{
fetch(`/api/days`)
},[])
return(
<div className="preview">
preview
</div>
)
}

View File

@@ -0,0 +1,31 @@
import { useEffect } from "react"
export default function Tracker({calendar,setDaySelected}){
useEffect(()=>{
fetch(`/api/days`)
},[])
const monthLine = Object.keys(calendar).map((month, monthIndex) =>
<div key={month} className="month">
<p>
{month.charAt(0).toUpperCase() + month.slice(1)}
</p>
<div className="monthBlock">
{[...Array(calendar[month])].map(
(dayValue, dayIndex) => (
<span onClick={()=>{setDaySelected(`${monthIndex+1}/${dayIndex+1}`)}} id={dayIndex} key={dayIndex} title={`${dayIndex+1} ${month}`} className="day"/>
)
)}
</div>
</div>
)
return(
<div className="Tracker">
{monthLine}
</div>
)
};

View File

@@ -0,0 +1,66 @@
import { useEffect, useState } from "react";
import { FileUploader } from "react-drag-drop-files";
const fileTypes = ["JPG", "PNG", "GIF"];
function DragDrop({setPreview ,file, setFile}) {
const handleChange = (file) => {
setFile(file);
};
useEffect(()=>{
if(!file) return;
const objectUrl = URL.createObjectURL(file)
setPreview(objectUrl)
// free memory when ever this component is unmounted
return () => URL.revokeObjectURL(objectUrl)
},[file])
return (
<FileUploader handleChange={handleChange} name="file" types={fileTypes} multiple={false}/>
);
}
export default function Upload({daySelected={daySelected}}){
const [file, setFile] = useState(null);
const [preview, setPreview] = useState(null);
function uploadFile(){
const formData = new FormData();
formData.append("file", file);
formData.append("date", daySelected)
fetch("/api/days", {
method: "POST",
body: formData,
}).then(() => {
setFile(null);
setPreview(null);
});
}
return(
<div className="Upload">
<div className="preview">
{preview ? <img src={preview} alt="preview" /> : null}
</div>
<DragDrop setPreview={setPreview} file={file} setFile={setFile}/>
<select type="select">
<option value="gesture">Gesture</option>
<option value="blender">Blender</option>
</select>
<p onClick={()=>{
uploadFile()
}}>Upload</p>
</div>
)
}

View File

@@ -0,0 +1,5 @@
export async function GET(req){
console.log(req)
return Response.json({"message":"hello"})
}

42
src/app/api/days/route.js Normal file
View File

@@ -0,0 +1,42 @@
import {readdir, access, mkdir, writeFile } from "fs/promises";
import path from "path";
export async function GET(req) {
const dir = "./public/uploads";
const entries = await readdir(dir, { withFileTypes: true });
console.log(entries)
return Response.json({"hello":"wesh"})
}
export async function POST(req) {
const formData = await req.formData();
const date = formData.get('date');
const file = formData.get('file');
console.log(file)
if (!file) {
return new Response("No file uploaded", { status: 400 });
}
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);
// if folder not created make one
try{
await access(`./public/uploads/${date}/`)
}catch{
await mkdir(`./public/uploads/${date}/`, {recursive:true})
}
//get the number of files before uploading
const files = await readdir(`./public/uploads/${date}/`);
const nbFiles = (files.length);
const fileName = `${nbFiles}.${path.extname(file.name).slice(1).toLowerCase()}`
console.log(fileName);
// Save file locally
const pathName = `./public/uploads/${date}/${fileName}`;
await writeFile(pathName, buffer);
return new Response("File uploaded successfully");
}

View File

@@ -0,0 +1,5 @@
export async function GET () {
const res = {"message":"hello"}
return Response.json(res)
}

View File

@@ -1,66 +1,46 @@
import Image from "next/image"; 'use client'
import styles from "./page.module.css";
import { useEffect, useState } from "react";
import "./style.sass"
import Preview from "./Component/preview";
import Upload from "./Component/upload";
import Tracker from "./Component/tracker";
export default function Home() { export default function Home() {
const calendar = {
"january":31,
"febuary":28,
"march":29,
"april":30,
"may":31,
"june":30,
"july":31,
"august":31,
"september":30,
"october":31,
"november":30,
"december":31,
}
const date = new Date();
const [daySelected, setDaySelected] = useState(`${date.getMonth()}/${date.getDate()}`)
return ( return (
<div className={styles.page}> <div className="Home">
<main className={styles.main}>
<Image <aside>
className={styles.logo} <p>Link</p>
src="/next.svg" </aside>
alt="Next.js logo"
width={100} <main>
height={20}
priority <h2>{daySelected}</h2>
/> <Preview daySelected={daySelected}/>
<div className={styles.intro}> <Upload daySelected={daySelected}/>
<h1>To get started, edit the page.js file.</h1> <Tracker calendar={calendar} setDaySelected={setDaySelected}/>
<p>
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className={styles.ctas}>
<a
className={styles.primary}
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className={styles.logo}
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className={styles.secondary}
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main> </main>
</div> </div>
); );
} }

View File

@@ -1,142 +0,0 @@
.page {
--background: #fafafa;
--foreground: #fff;
--text-primary: #000;
--text-secondary: #666;
--button-primary-hover: #383838;
--button-secondary-hover: #f2f2f2;
--button-secondary-border: #ebebeb;
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: var(--font-geist-sans);
background-color: var(--background);
}
.main {
display: flex;
flex: 1;
width: 100%;
max-width: 800px;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
background-color: var(--foreground);
padding: 120px 60px;
}
.intro {
display: flex;
flex-direction: column;
align-items: flex-start;
text-align: left;
gap: 24px;
}
.intro h1 {
max-width: 320px;
font-size: 40px;
font-weight: 600;
line-height: 48px;
letter-spacing: -2.4px;
text-wrap: balance;
color: var(--text-primary);
}
.intro p {
max-width: 440px;
font-size: 18px;
line-height: 32px;
text-wrap: balance;
color: var(--text-secondary);
}
.intro a {
font-weight: 500;
color: var(--text-primary);
}
.ctas {
display: flex;
flex-direction: row;
width: 100%;
max-width: 440px;
gap: 16px;
font-size: 14px;
}
.ctas a {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
padding: 0 16px;
border-radius: 128px;
border: 1px solid transparent;
transition: 0.2s;
cursor: pointer;
width: fit-content;
font-weight: 500;
}
a.primary {
background: var(--text-primary);
color: var(--background);
gap: 8px;
}
a.secondary {
border-color: var(--button-secondary-border);
}
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
a.primary:hover {
background: var(--button-primary-hover);
border-color: transparent;
}
a.secondary:hover {
background: var(--button-secondary-hover);
border-color: transparent;
}
}
@media (max-width: 600px) {
.main {
padding: 48px 24px;
}
.intro {
gap: 16px;
}
.intro h1 {
font-size: 32px;
line-height: 40px;
letter-spacing: -1.92px;
}
}
@media (prefers-color-scheme: dark) {
.logo {
filter: invert();
}
.page {
--background: #000;
--foreground: #000;
--text-primary: #ededed;
--text-secondary: #999;
--button-primary-hover: #ccc;
--button-secondary-hover: #1a1a1a;
--button-secondary-border: #1a1a1a;
}
}

43
src/app/style.sass Normal file
View File

@@ -0,0 +1,43 @@
.Home
display: flex
width: 100%
padding:2%
justify-content: space-between
height: 100vh
aside
background: rebeccapurple
width: 20%
height: 100%
main
width: 79%
height: 100%
padding: 1%
.Tracker
.month
display: flex
flex-direction: row
align-items: center
gap: 5px
p
width: 8%
.monthBlock
display: flex
gap: 5px
width: 90%
.day
display: block
width: 20px
height: 10px
background: green
border-radius: 5px
cursor: pointer
.Upload
display: flex
gap: 5%
align-items: center
.preview
height: 400px
img
height: 100%
select
height: 40px