init project portal web

This commit is contained in:
Sweli Giri
2025-04-15 13:56:54 +07:00
parent 9a25243035
commit 8b15dcebf8
122 changed files with 13965 additions and 1 deletions

10
app/api/cookies/route.ts Normal file
View File

@ -0,0 +1,10 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
export async function GET() {
const cookieStore = cookies();
const credential = JSON.parse(cookieStore.get("credential")?.value || "");
return NextResponse.json({ credential });
}

55
app/api/login/route.ts Normal file
View File

@ -0,0 +1,55 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server';
export const POST = async (request: NextRequest) => {
// INTERPOLATING API URL OF BACKEND
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/auth/login`
// GET REQUEST BODY
const {username, password} = await request.json();
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
data: JSON.stringify({
userName: username,
password,
})
})
if (response?.data ?? false) {
const token: string = response.data.data.token;
const expired = response.data.data.expired;
const data = {
username: response.data.data.username,
token
}
cookies().set({
name: 'credential',
value: JSON.stringify(data),
httpOnly: false,
sameSite: 'strict',
path: '/',
maxAge: expired
})
}
return NextResponse.json(response.data, { status: response.status });
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

18
app/api/logout/route.ts Normal file
View File

@ -0,0 +1,18 @@
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
export const POST = async () => {
// Hapus cookie 'credential'
cookies().delete({
name: "credential",
httpOnly: false,
sameSite: 'strict',
path: '/',
maxAge: 0,
})
// Return a JSON response
return NextResponse.json({
message: 'Cookie deleted successfully.',
status: 'success',
}, { status: 200 });
};

View File

@ -0,0 +1,36 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server';
export const POST = async () => {
// INTERPOLATING API URL OF BACKEND
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/usage_event/list`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,58 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server';
export const POST = async (request: NextRequest) => {
// INTERPOLATING API URL OF BACKEND
const {
offerType,
offerName,
pricePlanCode,
remarks,
sourceFrom,
baseValidPeriod,
serviceType,
versionValidPeriod
} = await request.json();
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/createpriceplan`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
data: JSON.stringify({
offerType,
offerName,
applyLevel: "S",
pricePlanCode,
remarks,
sourceFrom,
baseValidPeriod,
versionValidPeriod,
serviceType
})
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,36 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server';
export const POST = async (request: NextRequest) => {
// INTERPOLATING API URL OF BACKEND
const {id} = await request.json();
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/delete/${id}`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,39 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server';
export const POST = async () => {
// INTERPOLATING API URL OF BACKEND
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/getmenuList`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
// console.log(token, 'cek token');
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,39 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server';
export const POST = async (request: NextRequest) => {
// INTERPOLATING API URL OF BACKEND
const {page, size, type} = await request.json();
let url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/list`
if(type) url += `/${type}`
url += `?page=${page}&size=${size}`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,36 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server';
export const POST = async () => {
// INTERPOLATING API URL OF BACKEND
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/getservtype`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,36 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server';
export const POST = async () => {
// INTERPOLATING API URL OF BACKEND
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/getalltype`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

View File

@ -0,0 +1,55 @@
import axios from 'axios';
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server';
export const POST = async (request: NextRequest) => {
// INTERPOLATING API URL OF BACKEND
const {
offerVerId,
offerType,
reId,
ratePlanName,
ratePlanCode,
remarks,
ratePlanType,
} = await request.json();
const url: string = `${process.env.NEXT_PUBLIC_API_URL}/priceplan/createpriceplan`
const data = JSON.parse(cookies().get('credential')?.value ?? "")
const token = `Bearer ${data.token}`
try {
// MAKE AN API REQUEST
/*
* - WE USE AXIOS INSTEAD OF FETCH
* - FETCH ALWAYS RETURNS "TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is already closed"
* WHEN WE RUN "response.json()"
*/
const response = await axios(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token
},
data: JSON.stringify({
offerVerId,
offerType,
reId,
ratePlanName,
ratePlanCode,
remarks,
ratePlanType,
})
})
return NextResponse.json(response.data, { status: response.status });
} catch (error: any) {
if (axios.isAxiosError(error)) {
return NextResponse.json(error.response?.data || { message: 'Unknown error' }, { status: error.response?.status || 500 });
}
return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
}
}

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

98
app/globals.css Normal file
View File

@ -0,0 +1,98 @@
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
--sidebar-background: 189 100% 31%;
--sidebar-foreground: 0 0% 98%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-family: "Outfit", sans-serif;
box-sizing: border-box;
}
}

19
app/layout.tsx Normal file
View File

@ -0,0 +1,19 @@
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "OCS Portal Web",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body suppressHydrationWarning={true}>{children}</body>
</html>
);
}

15
app/main/layout.tsx Normal file
View File

@ -0,0 +1,15 @@
import MainLayout from "@/components/layout/main-layout"
interface Props {
children: React.ReactNode
}
const Layout = ({
children
}: Props) => {
return (
<MainLayout>{children}</MainLayout>
)
}
export default Layout

View File

@ -0,0 +1,16 @@
import PricePlanDetail from "@/lib/price-plan-detail/view"
interface Props {
params: {
id: string
}
}
const Page = ({params}: Props) => {
const id = params.id
return (
<PricePlanDetail id={id}/>
)
}
export default Page

View File

@ -0,0 +1,9 @@
import PricePlanList from "@/lib/price-plan/view"
const PricePlan = () => {
return (
<PricePlanList/>
)
}
export default PricePlan

View File

@ -0,0 +1,10 @@
import Login from "@/lib/login/view"
const LoginPage = () => {
return (
<Login/>
)
}
export default LoginPage

11
app/page.tsx Normal file
View File

@ -0,0 +1,11 @@
import AppBar from "@/components/module/app-bar"
import HomeView from "@/lib/home/view"
export default function homePage() {
return (
<div className="bg-[#F2F8F4] w-full h-screen">
<AppBar useLogo/>
<HomeView />
</div>
)
}