first commit
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
12
src/app/Component/preview.jsx
Normal file
12
src/app/Component/preview.jsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { useEffect } from "react"
|
||||||
|
|
||||||
|
export default function Preview({daySelected}){
|
||||||
|
useEffect(()=>{
|
||||||
|
fetch(`/api/days`)
|
||||||
|
},[])
|
||||||
|
return(
|
||||||
|
<div className="preview">
|
||||||
|
preview
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
31
src/app/Component/tracker.jsx
Normal file
31
src/app/Component/tracker.jsx
Normal 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>
|
||||||
|
)
|
||||||
|
};
|
||||||
66
src/app/Component/upload.jsx
Normal file
66
src/app/Component/upload.jsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
src/app/api/days/[id]/route.js
Normal file
5
src/app/api/days/[id]/route.js
Normal 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
42
src/app/api/days/route.js
Normal 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");
|
||||||
|
}
|
||||||
5
src/app/api/hello/route.js
Normal file
5
src/app/api/hello/route.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export async function GET () {
|
||||||
|
const res = {"message":"hello"}
|
||||||
|
return Response.json(res)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
43
src/app/style.sass
Normal 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
|
||||||
Reference in New Issue
Block a user