diff --git a/client/components/app/User/Card.jsx b/client/components/app/User/Card.jsx
new file mode 100644
index 0000000..42e3cf9
--- /dev/null
+++ b/client/components/app/User/Card.jsx
@@ -0,0 +1,53 @@
+import { CalendarFold, Trash, SquareArrowOutUpRight } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import Hover from '@/components/app/User/Hover';
+import Link from 'next/link';
+
+export default function Card({ index, user }) {
+ return (
+
+ );
+}
diff --git a/client/components/app/UserCard/Hover.jsx b/client/components/app/User/Hover.jsx
similarity index 99%
rename from client/components/app/UserCard/Hover.jsx
rename to client/components/app/User/Hover.jsx
index 87f26fa..58bf91b 100644
--- a/client/components/app/UserCard/Hover.jsx
+++ b/client/components/app/User/Hover.jsx
@@ -36,4 +36,4 @@ export default function Hover({ user }) {
);
-}
\ No newline at end of file
+}
diff --git a/client/components/app/UserCard/index.jsx b/client/components/app/User/Info.jsx
similarity index 83%
rename from client/components/app/UserCard/index.jsx
rename to client/components/app/User/Info.jsx
index 94e28e7..309bd69 100644
--- a/client/components/app/UserCard/index.jsx
+++ b/client/components/app/User/Info.jsx
@@ -1,6 +1,6 @@
-import Hover from '@/components/app/UserCard/Hover';
+import Hover from '@/components/app/User/Hover';
-export default function UserCard({ user }) {
+export default function UserInfo({ user }) {
return (
diff --git a/client/components/app/User/List.jsx b/client/components/app/User/List.jsx
new file mode 100644
index 0000000..331adc5
--- /dev/null
+++ b/client/components/app/User/List.jsx
@@ -0,0 +1,87 @@
+import { useState, useEffect } from 'react';
+import { LoaderCircle } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { useToast } from '@/components/ui/use-toast';
+import Card from '@/components/app/User/Card';
+
+export default function UserList({ fetchUsers }) {
+ const [users, setUsers] = useState([]);
+ const [offset, setOffset] = useState(10);
+ const [hasMoreUser, setHasMoreUser] = useState(true);
+ const { toast } = useToast();
+
+ const loadMoreUsers = async () => {
+ if (!hasMoreUser) return;
+
+ const response = await fetchUsers(offset);
+ if (!response) {
+ toast({
+ title: 'hay aksi, bir şeyler ters gitti!',
+ description:
+ 'sunucudan yanıt alınamadı. lütfen daha sonra tekrar deneyin.',
+ duration: 3000
+ });
+ return;
+ }
+
+ const newUsers = response.data.users || [];
+
+ if (newUsers.length > 10) {
+ setUsers((prevUsers) => [...prevUsers, ...newUsers.slice(0, 10)]);
+ } else {
+ setUsers((prevUsers) => [...prevUsers, ...newUsers]);
+ setHasMoreUser(false);
+ }
+
+ setOffset((prevOffset) => prevOffset + 10);
+ };
+
+ useEffect(
+ () => {
+ const fetchInitialUsers = async () => {
+ const response = await fetchUsers(0);
+ if (!response) {
+ toast({
+ title: 'hay aksi, bir şeyler ters gitti!',
+ description:
+ 'sunucudan yanıt alınamadı. lütfen daha sonra tekrar deneyin.',
+ duration: 3000
+ });
+ return;
+ }
+
+ const initialUsers = response.data.users || [];
+
+ if (initialUsers.length > 10) {
+ setUsers(initialUsers.slice(0, 10));
+ } else {
+ setUsers(initialUsers);
+ setHasMoreUser(false);
+ }
+ };
+
+ fetchInitialUsers();
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
+ return (
+
+ {users.length > 0 ? (
+ <>
+ {users.map((user, index) => (
+
+ ))}
+ {hasMoreUser && (
+
+ daha fazla göster
+
+ )}
+ >
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/client/lib/api/users/index.js b/client/lib/api/users/index.js
index e9b6626..24083ee 100644
--- a/client/lib/api/users/index.js
+++ b/client/lib/api/users/index.js
@@ -9,6 +9,15 @@ export async function getUser({ slug }) {
}
}
+export async function getUsers(limit, offset) {
+ try {
+ const response = await api.get('/users', { params: { limit, offset } });
+ return response;
+ } catch (error) {
+ return error.response;
+ }
+}
+
export async function getUserPosts(slug, limit, offset) {
try {
const response = await api.get(`/users/${slug}/posts`, {
diff --git a/client/tailwind.config.js b/client/tailwind.config.js
index 05b38f7..f2041a1 100644
--- a/client/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -4,83 +4,83 @@ module.exports = {
'./pages/**/*.{js,jsx}',
'./components/**/*.{js,jsx}',
'./app/**/*.{js,jsx}',
- './src/**/*.{js,jsx}',
+ './src/**/*.{js,jsx}'
],
- prefix: "",
+ prefix: '',
theme: {
container: {
center: true,
- padding: "2rem",
+ padding: '2rem',
screens: {
- "2xl": "1400px",
- },
+ '2xl': '1400px'
+ }
},
extend: {
colors: {
- border: "hsl(var(--border))",
- input: "hsl(var(--input))",
- ring: "hsl(var(--ring))",
- background: "hsl(var(--background))",
- foreground: "hsl(var(--foreground))",
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
primary: {
- DEFAULT: "hsl(var(--primary))",
- foreground: "hsl(var(--primary-foreground))",
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
- DEFAULT: "hsl(var(--secondary))",
- foreground: "hsl(var(--secondary-foreground))",
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))'
},
destructive: {
- DEFAULT: "hsl(var(--destructive))",
- foreground: "hsl(var(--destructive-foreground))",
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))'
},
muted: {
- DEFAULT: "hsl(var(--muted))",
- foreground: "hsl(var(--muted-foreground))",
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))'
},
accent: {
- DEFAULT: "hsl(var(--accent))",
- foreground: "hsl(var(--accent-foreground))",
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))'
},
popover: {
- DEFAULT: "hsl(var(--popover))",
- foreground: "hsl(var(--popover-foreground))",
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))'
},
card: {
- DEFAULT: "hsl(var(--card))",
- foreground: "hsl(var(--card-foreground))",
- },
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))'
+ }
},
borderRadius: {
- lg: "var(--radius)",
- md: "calc(var(--radius) - 2px)",
- sm: "calc(var(--radius) - 4px)",
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)'
},
keyframes: {
- "accordion-down": {
- from: { height: "0" },
- to: { height: "var(--radix-accordion-content-height)" },
+ 'accordion-down': {
+ from: { height: '0' },
+ to: { height: 'var(--radix-accordion-content-height)' }
},
- "accordion-up": {
- from: { height: "var(--radix-accordion-content-height)" },
- to: { height: "0" },
+ 'accordion-up': {
+ from: { height: 'var(--radix-accordion-content-height)' },
+ to: { height: '0' }
},
sparkle: {
'0%': { transform: 'scale(1.2) rotate(0deg)' },
'25%': { transform: 'scale(0.7) rotate(10deg)' },
'50%': { transform: 'scale(1.2) rotate(0deg)' },
'75%': { transform: 'scale(0.7) rotate(-10deg)' },
- '100%': { transform: 'scale(1.2) rotate(0deg)' },
- },
+ '100%': { transform: 'scale(1.2) rotate(0deg)' }
+ }
},
animation: {
- "accordion-down": "accordion-down 0.2s ease-out",
- "accordion-up": "accordion-up 0.2s ease-out",
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
sparkle1: 'sparkle 2s ease-in-out infinite',
sparkle2: 'sparkle 2.5s ease-in-out infinite',
- sparkle3: 'sparkle 3s ease-in-out infinite',
- },
- },
+ sparkle3: 'sparkle 3s ease-in-out infinite'
+ }
+ }
},
- plugins: [require("tailwindcss-animate")],
-}
\ No newline at end of file
+ plugins: [require('tailwindcss-animate')]
+};
diff --git a/server/handlers/users/get_users.go b/server/handlers/users/get_users.go
new file mode 100644
index 0000000..8b9f05c
--- /dev/null
+++ b/server/handlers/users/get_users.go
@@ -0,0 +1,31 @@
+package users
+
+import (
+ "github.com/gofiber/fiber/v2"
+ "github.com/lareii/copl.uk/server/models"
+)
+
+func GetUsers(c *fiber.Ctx) error {
+ limit := c.QueryInt("limit", 10)
+ offset := c.QueryInt("offset", 0)
+
+ users, err := models.GetUsers(int64(limit), int64(offset))
+ if err != nil {
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "message": "Error fetching users.",
+ })
+ }
+
+ var responseUsers []models.User
+ for _, user := range users {
+ user.Email = ""
+ user.Password = ""
+
+ responseUsers = append(responseUsers, user)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(fiber.Map{
+ "message": "Users found.",
+ "users": responseUsers,
+ })
+}
diff --git a/server/models/user.go b/server/models/user.go
index d3d6b4a..1c56ed0 100644
--- a/server/models/user.go
+++ b/server/models/user.go
@@ -12,6 +12,7 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
+ "go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
@@ -51,6 +52,25 @@ func GetUserByUsername(username string) (user User, err error) {
return user, err
}
+func GetUsers(limit, offset int64) ([]User, error) {
+ var users []User
+ cursor, err := database.Users.Find(context.Background(), bson.M{}, &options.FindOptions{
+ Limit: &limit,
+ Skip: &offset,
+ Sort: bson.M{"points": -1},
+ })
+ if err != nil {
+ return users, fmt.Errorf("error fetching users: %v", err)
+ }
+
+ err = cursor.All(context.Background(), &users)
+ if err != nil {
+ return users, fmt.Errorf("error decoding users: %v", err)
+ }
+
+ return users, nil
+}
+
func ValidateUser(c *fiber.Ctx) AuthStatus {
cookie := c.Cookies("jwt")
token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
diff --git a/server/router/router.go b/server/router/router.go
index f9524f5..c17d89e 100644
--- a/server/router/router.go
+++ b/server/router/router.go
@@ -20,6 +20,7 @@ func SetupRouter(app *fiber.App) {
authGroup.Get("/me", middlewares.AuthMiddleware(), auth.User)
userGroup := app.Group("/users")
+ userGroup.Get("/", middlewares.AuthMiddleware(), users.GetUsers)
userGroup.Get("/:slug", middlewares.AuthMiddleware(), users.GetUser)
userGroup.Get("/:slug/posts", middlewares.AuthMiddleware(), users.GetUserPosts)