diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx
index eb155e3..1986306 100644
--- a/frontend/src/components/NavBarItem.tsx
+++ b/frontend/src/components/NavBarItem.tsx
@@ -1,6 +1,5 @@
-import React, {useEffect, useRef} from 'react'
+import React, {useEffect, useRef, useState} from 'react'
import Link from 'next/link'
-import { useState } from 'react'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import BaseDivider from './BaseDivider'
import BaseIcon from './BaseIcon'
@@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) {
}
return
{NavBarItemComponentContents}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/components/Posts/SocialPostCard.tsx b/frontend/src/components/Posts/SocialPostCard.tsx
new file mode 100644
index 0000000..2393000
--- /dev/null
+++ b/frontend/src/components/Posts/SocialPostCard.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import { mdiHeart, mdiHeartOutline, mdiChatOutline, mdiSendOutline, mdiBookmarkOutline } from '@mdi/js';
+import BaseIcon from '../BaseIcon';
+import UserAvatar from '../UserAvatar';
+import dataFormatter from '../../helpers/dataFormatter';
+import Link from 'next/link';
+
+type Props = {
+ post: any;
+ onLike?: (id: string) => void;
+ onComment?: (id: string) => void;
+};
+
+export default function SocialPostCard({ post, onLike, onComment }: Props) {
+ const imageUrl = post.images && post.images[0] ? post.images[0].publicUrl : null;
+ const authorName = post.author ? `${post.author.firstName} ${post.author.lastName}` : 'Anonymous';
+ const authorAvatar = post.author ? post.author.avatar : null;
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
{authorName}
+ {post.location_name &&
{post.location_name}
}
+
+
+
+
+
+ {/* Image/Video */}
+
+ {imageUrl ? (
+

+ ) : (
+
No media
+ )}
+
+
+ {/* Interactions */}
+
+
+
+
+
+
+
+
+
+
+ {/* Likes Count */}
+
+ {post.post_reactions_count || 0} likes
+
+
+ {/* Caption */}
+
+ {authorName}
+ {post.caption}
+
+
+ {/* View Comments */}
+ {post.post_comments_count > 0 && (
+
+ )}
+
+ {/* Time */}
+
+ {dataFormatter.dateTimeFormatter(post.published_at)}
+
+
+
+ );
+}
diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx
index 1b9907d..26c3572 100644
--- a/frontend/src/layouts/Authenticated.tsx
+++ b/frontend/src/layouts/Authenticated.tsx
@@ -1,5 +1,4 @@
-import React, { ReactNode, useEffect } from 'react'
-import { useState } from 'react'
+import React, { ReactNode, useEffect, useState } from 'react'
import jwt from 'jsonwebtoken';
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
import menuAside from '../menuAside'
@@ -126,4 +125,4 @@ export default function LayoutAuthenticated({
)
-}
+}
\ No newline at end of file
diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts
index 58459f5..21e7bf0 100644
--- a/frontend/src/menuAside.ts
+++ b/frontend/src/menuAside.ts
@@ -2,6 +2,11 @@ import * as icon from '@mdi/js';
import { MenuAsideItem } from './interfaces'
const menuAside: MenuAsideItem[] = [
+ {
+ href: '/feed',
+ icon: icon.mdiHomeOutline,
+ label: 'Feed',
+ },
{
href: '/dashboard',
icon: icon.mdiViewDashboardOutline,
@@ -232,4 +237,4 @@ const menuAside: MenuAsideItem[] = [
},
]
-export default menuAside
+export default menuAside
\ No newline at end of file
diff --git a/frontend/src/pages/feed.tsx b/frontend/src/pages/feed.tsx
new file mode 100644
index 0000000..e6c1ae1
--- /dev/null
+++ b/frontend/src/pages/feed.tsx
@@ -0,0 +1,61 @@
+import React, { useEffect } from 'react';
+import type { ReactElement } from 'react';
+import Head from 'next/head';
+import LayoutAuthenticated from '../layouts/Authenticated';
+import SectionMain from '../components/SectionMain';
+import { getPageTitle } from '../config';
+import { useAppDispatch, useAppSelector } from '../stores/hooks';
+import { fetch as fetchPosts } from '../stores/posts/postsSlice';
+import SocialPostCard from '../components/Posts/SocialPostCard';
+import LoadingSpinner from '../components/LoadingSpinner';
+
+export default function FeedPage() {
+ const dispatch = useAppDispatch();
+ const { posts, loading } = useAppSelector((state) => state.posts);
+
+ useEffect(() => {
+ dispatch(fetchPosts({ page: 1, limit: 10 }));
+ }, [dispatch]);
+
+ return (
+ <>
+
+ {getPageTitle('Feed')}
+
+
+
+
Your Feed
+
+ {loading && (
+
+
+
+ )}
+
+ {!loading && posts && Array.isArray(posts) && posts.length > 0 && (
+
+ {posts.map((post: any) => (
+ console.log('Like', id)}
+ onComment={(id) => console.log('Comment', id)}
+ />
+ ))}
+
+ )}
+
+ {!loading && (!posts || (Array.isArray(posts) && posts.length === 0)) && (
+
+
No posts yet. Be the first to share something!
+
+ )}
+
+
+ >
+ );
+}
+
+FeedPage.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
\ No newline at end of file
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index 4330860..f1a17fe 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -1,166 +1,172 @@
-
import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import BaseButton from '../components/BaseButton';
-import CardBox from '../components/CardBox';
import SectionFullScreen from '../components/SectionFullScreen';
import LayoutGuest from '../layouts/Guest';
-import BaseDivider from '../components/BaseDivider';
-import BaseButtons from '../components/BaseButtons';
import { getPageTitle } from '../config';
import { useAppSelector } from '../stores/hooks';
-import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
-import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
+import { getPexelsImage } from '../helpers/pexels';
+import BaseIcon from '../components/BaseIcon';
+import { mdiInstagram, mdiLightningBolt, mdiEarth, mdiAccountGroup } from '@mdi/js';
+export default function LandingPage() {
+ const [illustrationImage, setIllustrationImage] = useState(null);
+ const { currentUser } = useAppSelector((state) => state.auth);
-export default function Starter() {
- const [illustrationImage, setIllustrationImage] = useState({
- src: undefined,
- photographer: undefined,
- photographer_url: undefined,
- })
- const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
- const [contentType, setContentType] = useState('image');
- const [contentPosition, setContentPosition] = useState('left');
- const textColor = useAppSelector((state) => state.style.linkColor);
-
- const title = 'Ultra Sosyal'
-
- // Fetch Pexels image/video
useEffect(() => {
async function fetchData() {
const image = await getPexelsImage();
- const video = await getPexelsVideo();
setIllustrationImage(image);
- setIllustrationVideo(video);
}
fetchData();
}, []);
- const imageBlock = (image) => (
-
-
+ return (
+
+
+
{getPageTitle('Ultra Sosyal - Connect. Share. Shine.')}
+
+
+ {/* Neon Glow Accents */}
+
+
+
+
+
+ {/* Header/Nav */}
+
+
+ {/* Hero Section */}
+
+
+
+
+ THE NEXT GENERATION OF SOCIAL
+
+
+ Share your
+
+ Digital Soul.
+
+
+
+ Experience a borderless world of connection. Posts, Stories, Reels, and real-time interaction, all in a stunning neon aesthetic.
+
+
+
+
+ {[1, 2, 3, 4].map((i) => (
+
+ U{i}
+
+ ))}
+
+ Joined by 20k+ creators
+
+
+
+
+
+ {/* Interactive Card Mockup */}
+
+
+
+ {illustrationImage && (
+

+ )}
+
+
+
+
+
+
+ {/* Features Grid */}
+
+ {[
+ { icon: mdiEarth, title: "Global Reach", desc: "Connect with people from every corner of the world instantly." },
+ { icon: mdiLightningBolt, title: "Hyper Fast", desc: "Built for speed. Real-time notifications and instant uploads." },
+ { icon: mdiAccountGroup, title: "Community Driven", desc: "Groups, forums, and shared collections to grow together." }
+ ].map((f, i) => (
+
+
+
+
+
{f.title}
+
{f.desc}
+
+ ))}
+
+
+ {/* Footer */}
+
+
+
);
-
- const videoBlock = (video) => {
- if (video?.video_files?.length > 0) {
- return (
-
-
-
-
)
- }
- };
-
- return (
-
-
-
{getPageTitle('Starter Page')}
-
-
-
-
- {contentType === 'image' && contentPosition !== 'background'
- ? imageBlock(illustrationImage)
- : null}
- {contentType === 'video' && contentPosition !== 'background'
- ? videoBlock(illustrationVideo)
- : null}
-
-
-
-
-
© 2026 {title}. All rights reserved
-
- Privacy Policy
-
-
-
-
- );
}
-Starter.getLayout = function getLayout(page: ReactElement) {
- return
{page};
-};
-
+LandingPage.getLayout = function getLayout(page: ReactElement) {
+ return
{page};
+};
\ No newline at end of file