login

// pages/login.js
import { useState } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { setToken, isAuthenticated } from "@/utils/auth";

import { Button } from "@/components/ui/button";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";

const formSchema = z.object({
  username: z.string().min(1, "Username is required"),
  password: z.string().min(5, "Password must be at least 5 characters long"),
});

export default function LoginPage({ serverError }) {
  const router = useRouter();
  const [errorMessage, setErrorMessage] = useState(serverError || "");
  const [isLoading, setIsLoading] = useState(false);

  const form = useForm({
    defaultValues: {
      username: "",
      password: "",
    },
    resolver: zodResolver(formSchema),
  });

  const onSubmit = async (data) => {
    setErrorMessage("");
    setIsLoading(true);

    try {
      const response = await fetch("http://localhost:8000/api/login/", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(data),
        credentials: 'include', // Penting untuk cookies
      });

      const result = await response.json();

      if (!response.ok) {
        throw new Error(result.message || "Login failed. Please check your credentials.");
      }

      // Simpan token di cookie dan localStorage
      setToken(result.access);

      router.push("/menu");
    } catch (error) {
      setErrorMessage(error.message);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    
      
        
          Log in to RestoKu
        

        {errorMessage && (
          
            {errorMessage}
          
        )}

        
          
             (
                
                  Username
                  
                    
                  
                  
                
              )}
            />
             (
                
                  Password
                  
                    
                  
                  
                
              )}
            />
            
              {isLoading ? "Logging in..." : "Login"}
            
          
        
        
          
            Forgot your password?
          
          
            Don't have an account?
            
              Create account
            
          
        
      
    
  );
}

export async function getServerSideProps(context) {
  // Cek jika pengguna sudah terautentikasi
  if (isAuthenticated(context)) {
    return {
      redirect: {
        destination: '/menu',
        permanent: false,
      },
    };
  }

  const { error } = context.query;

  return {
    props: {
      serverError: error || null,
    },
  };
}

utils/auth

// utils/auth.js
import Cookies from 'js-cookie';
import { parseCookies } from 'nookies';

// Fungsi untuk menyimpan token di cookies dan localStorage (untuk kompatibilitas)
export const setToken = (token) => {
  if (typeof window !== 'undefined') {
    // Client-side
    localStorage.setItem('adminToken', token);
    // Mengatur cookie dengan secure flag, httpOnly untuk produksi
    Cookies.set('adminToken', token, { expires: 7 }); // Expires in 7 days
  }
};

// Fungsi untuk mendapatkan token
export const getToken = (ctx) => {
  // Server-side
  if (ctx) {
    const cookies = parseCookies(ctx);
    return cookies.adminToken;
  }

  // Client-side
  if (typeof window !== 'undefined') {
    return Cookies.get('adminToken') || localStorage.getItem('adminToken');
  }

  return null;
};

// Fungsi untuk menghapus token saat logout
export const removeToken = () => {
  if (typeof window !== 'undefined') {
    localStorage.removeItem('adminToken');
    Cookies.remove('adminToken');
  }
};

// Fungsi untuk memeriksa apakah pengguna sudah terautentikasi
export const isAuthenticated = (ctx) => {
  const token = getToken(ctx);
  return !!token;
};

menu

// pages/menu.tsx
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { PlusIcon } from "lucide-react";
import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { getToken, removeToken } from "@/utils/auth";
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

interface Menuitem {
  id_menu: number;
  nama_menu: string;
  harga: number;
  menu_image: string;
  id_kategori: number;
}

async function getMenuData() {
  const token = getToken({ req: { headers: { cookie: cookies().toString() } });

  if (!token) {
    redirect('/login');
  }

  try {
    const response = await fetch("http://127.0.0.1:8000/api/menu/", {
      headers: {
        Authorization: `Bearer ${token}`,
      },
      cache: 'no-store'
    });

    if (response.status === 401) {
      removeToken();
      redirect('/login');
    }

    if (!response.ok) {
      throw new Error('Failed to fetch menu data');
    }

    return await response.json();
  } catch (error) {
    console.error("Error fetching menu data:", error);
    return [];
  }
}

export default async function MenuPage() {
  const menus: Menuitem[] = await getMenuData();

  async function handleAddMenu(formData: FormData) {
    'use server';

    const token = getToken({ req: { headers: { cookie: cookies().toString() } });

    if (!token) {
      redirect('/login');
    }

    const namaMenu = formData.get('nama_menu') as string;
    const harga = formData.get('harga') as string;
    const kategori = formData.get('id_kategori') as string;
    const imageFile = formData.get('menu_image') as File;

    const postData = new FormData();
    postData.append('nama_menu', namaMenu);
    postData.append('harga', harga);
    postData.append('id_kategori', kategori);

    if (imageFile.size > 0) {
      postData.append('menu_image', imageFile);
    }

    try {
      const response = await fetch("http://127.0.0.1:8000/api/menu/", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${token}`,
        },
        body: postData,
      });

      if (response.status === 401) {
        removeToken();
        redirect('/login');
      }

      if (!response.ok) {
        throw new Error('Failed to add menu');
      }

      redirect('/menu');
    } catch (error) {
      console.error("Error adding menu:", error);
      throw error;
    }
  }

  return (
    
      
        
          Daftar Menu
        
        
          
            Logout
          
        
      

      
        
          
             Tambah Menu
          
        
        
          
            Tambah Menu Baru
          

          
            
              
                
                  Nama
                
                
              
              
                
                  Harga
                
                
              
              
                
                  Kategori
                
                
              
              
                
                  Gambar
                
                
              
            

            
              Simpan
            
          
        
      

      
        {menus.map((menu) => (
          
            
              
                {menu.nama_menu}
              
              
                Rp {menu.harga.toLocaleString()}
              
            
            
              
                
              
            
          
        ))}
      
    
  );
}

meja

// app/features-02/page.tsx
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { PlusIcon } from "lucide-react";
import { getToken, removeToken } from "@/utils/auth";
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

interface MejaItem {
  id_meja: number;
  no_meja: number;
  kapasitas: number;
  image_meja: string;
}

async function getMejaData() {
  const token = getToken({ req: { headers: { cookie: cookies().toString() } });

  if (!token) {
    redirect('/login');
  }

  try {
    const response = await fetch("http://127.0.0.1:8000/api/meja/", {
      headers: {
        Authorization: `Bearer ${token}`,
      },
      cache: 'no-store'
    });

    if (response.status === 401) {
      removeToken();
      redirect('/login');
    }

    if (!response.ok) {
      throw new Error('Failed to fetch table data');
    }

    return await response.json();
  } catch (error) {
    console.error("Error fetching table data:", error);
    return [];
  }
}

export default async function Features02Page() {
  const mejas: MejaItem[] = await getMejaData();

  async function handleAddTable(formData: FormData) {
    'use server';

    const token = getToken({ req: { headers: { cookie: cookies().toString() } });

    if (!token) {
      redirect('/login');
    }

    const no_meja = formData.get('no_meja') as string;
    const kapasitas = formData.get('kapasitas') as string;
    const imageFile = formData.get('image_meja') as File;

    const postData = new FormData();
    postData.append('no_meja', no_meja);
    postData.append('kapasitas', kapasitas);

    if (imageFile.size > 0) {
      postData.append('image_meja', imageFile);
    }

    try {
      const response = await fetch("http://127.0.0.1:8000/api/meja/", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${token}`,
        },
        body: postData,
      });

      if (response.status === 401) {
        removeToken();
        redirect('/login');
      }

      if (!response.ok) {
        throw new Error('Failed to add table');
      }

      redirect('/features-02');
    } catch (error) {
      console.error("Error adding table:", error);
      throw error;
    }
  }

  return (
    
      
        
          
            Daftar Meja
          
          
            
              Logout
            
          
        

        
          
            
              
                 Tambah Meja
              
            
            
              
                Tambah Meja Baru
              

              
                
                  
                    
                      No Meja
                    
                    
                  
                  
                    
                      Kapasitas
                    
                    
                  
                  
                    
                      Gambar
                    
                    
                  
                

                
                  Simpan
                
              
            
          
        

        
          {mejas.map((meja) => (
            
              
                
              
              
                Nomor Meja: {meja.no_meja}
              
              
                Kapasitas: {meja.kapasitas} orang
              
            
          ))}
        
      
    
  );
}