some changes

This commit is contained in:
2025-03-04 15:00:34 +01:00
parent 4506bbf17a
commit 2037b0a177
16 changed files with 102 additions and 230 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import { TwitterApi } from 'twitter-api-v2'; import { TwitterApi } from 'twitter-api-v2';
import config from '@/lib/config'; import config from '@/lib/config';
import { inspect } from 'util'; import { inspect } from 'util';
import { Post } from '@/utils'; import { Post } from '@/lib/types';
import database from '../../../lib/database'; import database from '../../../lib/database';
import { format } from 'date-fns'; import { format } from 'date-fns';
import cron from 'node-cron'; import cron from 'node-cron';
+3 -3
View File
@@ -1,6 +1,6 @@
import { NextResponse, NextRequest } from 'next/server'; import { NextResponse, NextRequest } from 'next/server';
import database from '../../../lib/database'; import database from '../../../lib/database';
import { PostRequest, ScheduleRequest } from '@/utils'; import { PostRequest, ScheduleRequest } from '@/lib/types';
import post from './post'; import post from './post';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { HttpStatusCode } from 'axios'; import { HttpStatusCode } from 'axios';
@@ -99,9 +99,9 @@ export async function GET(req: NextRequest) {
if (!sessionId) if (!sessionId)
return NextResponse.json( return NextResponse.json(
{ {
message: 'Invalid session', message: 'No session',
}, },
{ status: HttpStatusCode.NotFound } { status: HttpStatusCode.Unauthorized }
); );
const session = await database.sessions.findOne({ const session = await database.sessions.findOne({
-98
View File
@@ -1,98 +0,0 @@
import { NextResponse, NextRequest } from 'next/server';
import database from '../../../lib/database';
import { ObjectId } from 'mongodb';
export async function GET(req: NextRequest) {
const sessionId = req.cookies.get('session_id');
console.log(sessionId);
if (!sessionId)
return NextResponse.json(
{
message: 'Session not found',
},
{ status: 401 }
);
const user = await database.sessions.findOne({
_id: new ObjectId(sessionId.value),
});
if (!user)
return NextResponse.json(
{
message: 'Invalid session',
},
{ status: 401 }
);
return NextResponse.json(
{
message: 'ok',
dashboard: {
engagement: [
{ week: 'Mo', views: 7300 },
{ week: 'Tuesday', views: 18000 },
{ week: 'Wednesday', views: 12000 },
{ week: 'Thursday', views: 12000 },
{ week: 'Friday', views: 23000 },
{ week: 'Saturday', views: 40000 },
{ week: 'Sunday', views: 20000 },
],
},
},
{ status: 200 }
);
}
// Account login
// Email exists
// Password is valid
export async function POST(req: NextRequest) {
const { email, password } = await req.json();
if (typeof email !== 'string' || typeof password !== 'string') {
return NextResponse.json(
{
message:
"Invalid structure, body requires 'email: string' and 'password: string'",
},
{ status: 400 }
);
}
const user = await database.users.findOne({ email });
if (!user || user.password !== password) {
return NextResponse.json(
{
message: 'Invalid email or password',
},
{ status: 401 }
);
}
const sessionId = new ObjectId();
await database.sessions.insertOne({
_id: sessionId,
user: user._id.toString(),
});
const response = NextResponse.json(
{
message: 'ok',
},
{ status: 200 }
);
response.cookies.set('session_id', sessionId.toString(), {
httpOnly: false,
secure: false,
sameSite: 'lax',
path: '/api',
maxAge: 60 * 60 * 24, // 1 day
});
return response;
}
+68 -55
View File
@@ -4,11 +4,8 @@ import {
ContextMenuItem, ContextMenuItem,
ContextMenuTrigger, ContextMenuTrigger,
} from '@/components/ui/context-menu'; } from '@/components/ui/context-menu';
import { ScheduleRemoveRequest } from '@/utils';
import axios from 'axios';
import { format } from 'date-fns';
import { PlusCircle, Trash2 } from 'lucide-react'; import { PlusCircle, Trash2 } from 'lucide-react';
import axios from 'axios';
export interface CalendarSchedule { export interface CalendarSchedule {
date: Date; date: Date;
@@ -16,84 +13,100 @@ export interface CalendarSchedule {
id: string; id: string;
} }
const weekdays: [string, number, number, number][] = [ // Function to get the current week's dates (Monday - Sunday)
['Mon', 2025, 2, 24], const getCurrentWeek = (): { label: string; date: Date }[] => {
['Tue', 2025, 2, 25], const today = new Date();
['Wed', 2025, 2, 26], const dayOfWeek = today.getDay(); // 0 (Sun) - 6 (Sat)
['Thu', 2025, 2, 27], const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Adjust for Monday start
['Fri', 2025, 2, 28],
['Sat', 2025, 2, 29],
['Sun', 2025, 3, 1],
];
const times: [string, number][] = [ return Array.from({ length: 7 }, (_, i) => {
['12 PM', 12], const date = new Date(today);
['1 PM', 13], date.setDate(today.getDate() + mondayOffset + i);
['2 PM', 14], return {
['3 PM', 15], label: date.toLocaleDateString('en-US', { weekday: 'short' }), // 'Mon', 'Tue', etc.
['4 PM', 16], date,
['5 PM', 17], };
['6 PM', 18], });
['7 PM', 19], };
['8 PM', 20],
['9 PM', 21], // Time slots from 12 PM to 11 PM
['10 PM', 22], const times: { label: string; hour: number }[] = Array.from(
['11 PM', 23], { length: 12 },
]; (_, i) => ({
label: `${(i + 12) % 12 || 12} PM`, // '12 PM', '1 PM', ..., '11 PM'
hour: i + 12,
})
);
export default function Calendar({ export default function Calendar({
setDialog, setDialog,
events, events,
setDate, setDate,
date,
setEvents, setEvents,
}: { }: {
setDialog: (b: boolean) => void; setDialog: (b: boolean) => void;
events: CalendarSchedule[]; events: CalendarSchedule[];
date: Date;
setDate: (d: Date) => void; setDate: (d: Date) => void;
setEvents: (events: CalendarSchedule[]) => void; setEvents: (events: CalendarSchedule[]) => void;
}) { }) {
const weekdays = getCurrentWeek();
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<div className="grid grid-cols-1 w-full h-20 mb-2"> <div className="grid grid-cols-1 w-full h-20 mb-2">
<div className="flex gap-2"> <div className="flex gap-2">
{/* Times column */} {/* Time Labels */}
<div className="w-20 flex flex-col gap-2"> <div className="w-20 flex flex-col gap-2">
<div className="h-24"></div>{' '} <div className="h-24"></div>{' '}
{/* Empty space for alignment */} {/* Empty space for alignment */}
<div className="grid grid-rows-10 h-full gap-2"> <div className="grid grid-rows-12 h-full gap-2">
{times.map((t, j) => ( {times.map((time) => (
<div <div
key={j} key={time.hour}
className="flex pr-2 h-16 border rounded-xl" className="flex pr-2 h-16 border rounded-xl"
> >
<p className="size-max m-auto">{t[0]}</p> <p className="size-max m-auto">
{time.label}
</p>
</div> </div>
))} ))}
</div> </div>
</div> </div>
{/* Calendar grid */} {/* Calendar Grid */}
<div className="grid grid-cols-7 gap-2 flex-1"> <div className="grid grid-cols-7 gap-2 flex-1">
{weekdays.map((w, i) => ( {weekdays.map((weekday) => (
<div key={i} className="flex flex-col h-full gap-2"> <div
key={weekday.date.toDateString()}
className="flex flex-col h-full gap-2"
>
{/* Day Header */}
<div className="border rounded-md h-24 flex"> <div className="border rounded-md h-24 flex">
<p className="size-max mx-auto">{w[0]}</p> <p className="size-max mx-auto">
{weekday.label}
</p>
</div> </div>
<div className="grid grid-rows-10 h-full gap-2">
{times.map((t, j) => { {/* Time Slots */}
const event = events.find((e) => { <div className="grid grid-rows-12 h-full gap-2">
return ( {times.map((time) => {
e.date.getFullYear() === w[1] && const event = events.find(
e.date.getMonth() === w[2] && (e) =>
e.date.getDate() === w[3] && e.date.getFullYear() ===
e.date.getHours() === t[1] weekday.date.getFullYear() &&
); e.date.getMonth() ===
}); weekday.date.getMonth() &&
e.date.getDate() ===
weekday.date.getDate() &&
e.date.getHours() === time.hour
);
return ( return (
<ContextMenu key={j}> <ContextMenu
key={`${weekday.date.toDateString()}-${
time.hour
}`}
>
<ContextMenuTrigger> <ContextMenuTrigger>
<div <div
className={`border rounded-xl w-full h-16 ${ className={`border rounded-xl w-full h-16 ${
@@ -104,7 +117,7 @@ export default function Calendar({
> >
{event && ( {event && (
<div className="p-2 text-sm flex items-center justify-center"> <div className="p-2 text-sm flex items-center justify-center">
{/* <LampIcon className="h-6 w-6" /> */} {/* Event indicator */}
</div> </div>
)} )}
</div> </div>
@@ -115,10 +128,10 @@ export default function Calendar({
setDialog(true); setDialog(true);
setDate( setDate(
new Date( new Date(
w[1], weekday.date.getFullYear(),
w[2], weekday.date.getMonth(),
w[3], weekday.date.getDate(),
t[1], time.hour,
0, 0,
0 0
) )
@@ -139,7 +152,7 @@ export default function Calendar({
params: { params: {
schedule: schedule:
event.id, event.id,
} satisfies ScheduleRemoveRequest, },
} }
) )
.then( .then(
+6 -8
View File
@@ -8,25 +8,27 @@ import PostDialog, { today } from './postDialog';
import axios from 'axios'; import axios from 'axios';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Calendar, { CalendarSchedule } from './calendar'; import Calendar, { CalendarSchedule } from './calendar';
import { DATE_FORMAT } from '@/lib/types';
export default function CalendarApp() { export default function CalendarApp() {
const [eventCount, setEventCount] = useState(0);
const sessionId = Cookies.get('session_id') || ''; const sessionId = Cookies.get('session_id') || '';
const [events, setEvents] = useState<CalendarSchedule[]>([]); const [events, setEvents] = useState<CalendarSchedule[]>([]);
useEffect(() => { useEffect(() => {
axios axios
.get(`/api/posts/?id=${sessionId}`, { .get(`/api/posts/`, {
withCredentials: true, withCredentials: true,
}) })
.then((response) => { .then((response) => {
const v = response.data.schedules.map((e: any) => ({ const v = response.data.schedules.map((e: any) => ({
date: parse(e.scheduled, 'yyyy-MM-dd HH:mm', new Date()), date: parse(e.scheduled, DATE_FORMAT, new Date()),
platforms: e.platforms, platforms: e.platforms,
id: e._id, id: e._id,
})); }));
setEvents(v); setEvents(v);
setEventCount(response.data.schedules.length); })
.catch((err) => {
console.error(err);
}); });
}, []); }, []);
@@ -43,16 +45,12 @@ export default function CalendarApp() {
setNewPostDialogOpen={setDialog} setNewPostDialogOpen={setDialog}
setEvents={(e) => { setEvents={(e) => {
setEvents([...events, e]); setEvents([...events, e]);
setEventCount(eventCount + 1);
}} }}
eventId={eventCount}
sessionId={sessionId}
/> />
<Calendar <Calendar
setDialog={setDialog} setDialog={setDialog}
events={events} events={events}
date={date}
setDate={setDate} setDate={setDate}
setEvents={setEvents} setEvents={setEvents}
/> />
+2 -6
View File
@@ -25,7 +25,7 @@ import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { useState } from 'react'; import { useState } from 'react';
import axios from 'axios'; import axios from 'axios';
import { PostRequest, ScheduleRequest } from '@/utils'; import { DATE_FORMAT, PostRequest, ScheduleRequest } from '@/lib/types';
import { CalendarSchedule } from './calendar'; import { CalendarSchedule } from './calendar';
type PostDialogProps = { type PostDialogProps = {
@@ -33,9 +33,7 @@ type PostDialogProps = {
setNewPostDialogOpen: (b: boolean) => void; setNewPostDialogOpen: (b: boolean) => void;
date: Date; date: Date;
setDate: (d: Date) => void; setDate: (d: Date) => void;
eventId: number;
setEvents: (e: CalendarSchedule) => void; setEvents: (e: CalendarSchedule) => void;
sessionId: string;
}; };
export const today = new Date(); export const today = new Date();
@@ -47,8 +45,6 @@ export default function PostDialog({
date, date,
setDate, setDate,
setEvents: addEvent, setEvents: addEvent,
eventId,
sessionId,
}: PostDialogProps) { }: PostDialogProps) {
const [content, setContent] = useState(''); const [content, setContent] = useState('');
@@ -147,7 +143,7 @@ export default function PostDialog({
const { data } = await axios.put( const { data } = await axios.put(
'/api/posts', '/api/posts',
{ {
scheduled: format(date, 'y-MM-dd hh:mm'), scheduled: format(date, DATE_FORMAT),
platforms: ['andjksds'], platforms: ['andjksds'],
content, content,
} satisfies ScheduleRequest, } satisfies ScheduleRequest,
+1 -1
View File
@@ -1,6 +1,6 @@
'use client'; 'use client';
import { PlatformKind } from '@/utils'; import { PlatformKind } from '@/lib/types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PlatformIcon } from '@/components/icons'; import { PlatformIcon } from '@/components/icons';
+1 -1
View File
@@ -101,7 +101,7 @@ export default function Engagement({
</AreaChart> </AreaChart>
</ChartContainer> </ChartContainer>
) : ( ) : (
<Skeleton className="w-full xl:w-[680px] lg:h-[270px] 2xl:w-[880px]" /> <Skeleton className="w-full xl:w-[680px] lg:h-[270px] 2xl:h-[350px] 2xl:w-[880px]" />
)} )}
</CardContent> </CardContent>
</Card> </Card>
+1 -1
View File
@@ -21,7 +21,7 @@ export default function Dashboard() {
useEffect(() => { useEffect(() => {
axios axios
.get('/api/session/') .get('/api/auth/')
.then((res) => { .then((res) => {
setData(res.data.dashboard); setData(res.data.dashboard);
}) })
+1 -1
View File
@@ -1,4 +1,4 @@
import { PlatformKind } from '@/utils'; import { PlatformKind } from '@/lib/types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PlatformIcon } from '@/components/icons'; import { PlatformIcon } from '@/components/icons';
import { format } from 'date-fns'; import { format } from 'date-fns';
+1 -1
View File
@@ -19,7 +19,7 @@ export default function Login() {
try { try {
const { sessionId } = ( const { sessionId } = (
await axios.post( await axios.post(
'/api/session/', '/api/auth/',
{ {
email, email,
password, password,
+1 -1
View File
@@ -1,4 +1,4 @@
import { PlatformKind } from '@/utils'; import { PlatformKind } from '@/lib/types';
interface PlatformProps extends React.SVGProps<SVGSVGElement> { interface PlatformProps extends React.SVGProps<SVGSVGElement> {
platform: PlatformKind; platform: PlatformKind;
+15 -1
View File
@@ -8,6 +8,8 @@ import {
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { useTheme } from 'next-themes'; import { useTheme } from 'next-themes';
import { Lightbulb, Settings } from 'lucide-react'; import { Lightbulb, Settings } from 'lucide-react';
import axios from 'axios';
import { redirect } from 'next/navigation';
export default function SettingsDropdown() { export default function SettingsDropdown() {
const { setTheme, theme } = useTheme(); const { setTheme, theme } = useTheme();
@@ -25,7 +27,19 @@ export default function SettingsDropdown() {
<DropdownMenuItem>Profile</DropdownMenuItem> <DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem> <DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuItem>Team</DropdownMenuItem> <DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuItem>Subscription</DropdownMenuItem> <DropdownMenuItem
onClick={() => {
axios
.delete('/api/auth/', {
withCredentials: true,
})
.then(() => {
redirect('/login');
});
}}
>
Log Out
</DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => onClick={() =>
setTheme(theme === 'light' ? 'dark' : 'light') setTheme(theme === 'light' ? 'dark' : 'light')
-2
View File
@@ -14,6 +14,4 @@ const config = {
mongoUrl: process.env.MONGO || error('MONGO'), mongoUrl: process.env.MONGO || error('MONGO'),
}; };
console.log(process.env.MONGO);
export default config; export default config;
+1 -1
View File
@@ -1,6 +1,6 @@
import { MongoClient } from 'mongodb'; import { MongoClient } from 'mongodb';
import config from './config'; import config from './config';
import { Schedule, Session, User } from '@/utils'; import { Schedule, Session, User } from '@/lib/types';
const client = new MongoClient(config.mongoUrl); const client = new MongoClient(config.mongoUrl);
-49
View File
@@ -1,49 +0,0 @@
/*
Request types
*/
export interface PostRequest {
content: string;
platforms: string[];
}
export interface ScheduleRequest extends PostRequest {
scheduled: string;
}
export interface ScheduleRemoveRequest {
schedule: string;
}
/*
Database types
*/
export type PlatformKind = 'x' | 'linkedin';
export interface Platform {
platform: PlatformKind;
token: string;
secret: string;
}
export type Platforms = { [key: string]: Platform };
export interface User {
name: string;
email: string;
password: string;
platforms: Platforms;
}
export interface Post {
account: string;
content: string;
platforms: string[];
}
export interface Schedule extends Post {
scheduled: string;
}
export type Session = { user: string };