add dashboard, feedback, faq
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import SidebarAdmin from "../Layout/SidebarAdmin";
|
||||
import NavbarAdmin from "../Layout/NavbarAdmin";
|
||||
import "froala-editor/js/plugins.pkgd.min.js";
|
||||
import "froala-editor/css/froala_editor.pkgd.min.css";
|
||||
import "froala-editor/css/froala_style.min.css";
|
||||
import FroalaEditor from "react-froala-wysiwyg";
|
||||
import axios from "axios";
|
||||
|
||||
const AddFaq = () => {
|
||||
const [questions, setQuestions] = useState("");
|
||||
const [answers, setAnswers] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const token = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("token="))
|
||||
?.split("=")[1];
|
||||
|
||||
if (!token) {
|
||||
alert("Token tidak ditemukan.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!questions || !answers) {
|
||||
alert("Semua field wajib diisi.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/faq-api/add`,
|
||||
{
|
||||
pertanyaan: questions,
|
||||
jawaban: answers,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data.status) {
|
||||
alert("Faq berhasil ditambahkan!");
|
||||
navigate("/faq-list");
|
||||
} else {
|
||||
alert(response.data.message || "Gagal menambahkan faq.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.response) {
|
||||
console.log("Error response data:", error.response.data);
|
||||
}
|
||||
console.error("Error adding faq:", error);
|
||||
alert("Terjadi kesalahan saat menambahkan faq.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full overflow-hidden">
|
||||
<NavbarAdmin />
|
||||
<div className="flex flex-row">
|
||||
<SidebarAdmin />
|
||||
<div className="p-10 bg-white flex-col flex-wrap w-full">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h2 className="text-xl font-semibold">
|
||||
Tambah Frequently Asked Questions
|
||||
</h2>
|
||||
<button
|
||||
className="bg-[#1F4A92] hover:bg-[#677c9f] cursor-pointer text-white px-6 py-2 rounded-md font-medium"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form className="flex flex-col gap-6 max-w-full">
|
||||
{/* Questions */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Questions</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
value={questions}
|
||||
onChange={(e) => setQuestions(e.target.value)}
|
||||
placeholder="Tulis pertanyaan kamu..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Answers */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Answers</label>
|
||||
<FroalaEditor
|
||||
tag="textarea"
|
||||
model={answers}
|
||||
onModelChange={(model: any) => setAnswers(model)}
|
||||
config={{
|
||||
placeholderText: "Tulis jawaban kamu...",
|
||||
charCounterCount: true,
|
||||
toolbarSticky: true,
|
||||
heightMin: 200,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddFaq;
|
||||
@@ -0,0 +1,178 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import SidebarAdmin from "../Layout/SidebarAdmin";
|
||||
import NavbarAdmin from "../Layout/NavbarAdmin";
|
||||
import "froala-editor/js/plugins.pkgd.min.js";
|
||||
import "froala-editor/css/froala_editor.pkgd.min.css";
|
||||
import "froala-editor/css/froala_style.min.css";
|
||||
import FroalaEditor from "react-froala-wysiwyg";
|
||||
import axios from "axios";
|
||||
|
||||
const AddFeedback = () => {
|
||||
const [posisi, setPosisi] = useState("");
|
||||
const [universitas, setUniversitas] = useState("");
|
||||
const [nama, setNama] = useState("");
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
const [batch, setBatch] = useState("");
|
||||
const [feedback, setFeedbacks] = useState("");
|
||||
const [year, setYear] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const token = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("token="))
|
||||
?.split("=")[1];
|
||||
|
||||
if (!token) {
|
||||
alert("Token tidak ditemukan.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nama || !universitas || !posisi || !batch || !year || !feedback) {
|
||||
alert("Semua field wajib diisi.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("nama", nama);
|
||||
formData.append("universitas", universitas);
|
||||
formData.append("posisi", posisi);
|
||||
formData.append("batch", batch);
|
||||
formData.append("tahun", year);
|
||||
formData.append("pesan", feedback);
|
||||
if (image) formData.append("image", image);
|
||||
|
||||
const response = await axios.post(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/feedback-api/add`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data.status) {
|
||||
alert("Feedback berhasil ditambahkan!");
|
||||
navigate("/feedback-list");
|
||||
} else {
|
||||
alert(response.data.message || "Gagal menambahkan feedback.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Error adding feedback:", error);
|
||||
alert("Terjadi kesalahan saat menambahkan feedback.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full overflow-hidden">
|
||||
<NavbarAdmin />
|
||||
<div className="flex flex-row">
|
||||
<SidebarAdmin />
|
||||
<div className="p-10 bg-white flex-col flex-wrap w-full">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h2 className="text-xl font-semibold">Tambah Feedback</h2>
|
||||
<button
|
||||
className="bg-[#1F4A92] hover:bg-[#677c9f] cursor-pointer text-white px-6 py-2 rounded-md font-medium"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form className="flex flex-col gap-6 max-w-full">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Nama</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
value={nama}
|
||||
onChange={(e) => setNama(e.target.value)}
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Universitas</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
value={universitas}
|
||||
onChange={(e) => setUniversitas(e.target.value)}
|
||||
placeholder="Telkom University Bandung"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Posisi</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
value={posisi}
|
||||
onChange={(e) => setPosisi(e.target.value)}
|
||||
placeholder="UI/UX Designer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Batch</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
value={batch}
|
||||
onChange={(e) => setBatch(e.target.value)}
|
||||
placeholder="5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Tahun</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
value={year}
|
||||
onChange={(e) => setYear(e.target.value)}
|
||||
placeholder="2025"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 col-span-2">
|
||||
<label className="text-sm font-medium">Image anda</label>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(e) =>
|
||||
setImage(e.target.files ? e.target.files[0] : null)
|
||||
}
|
||||
className="border border-gray-300 rounded-lg p-3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Isi Konten */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-sm font-medium">Isi Konten</label>
|
||||
<FroalaEditor
|
||||
tag="textarea"
|
||||
model={feedback}
|
||||
onModelChange={(model: any) => setFeedbacks(model)}
|
||||
config={{
|
||||
placeholderText: "Tulis feedback kamu...",
|
||||
charCounterCount: true,
|
||||
toolbarSticky: true,
|
||||
heightMin: 200,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddFeedback;
|
||||
@@ -0,0 +1,372 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { PieChart, Pie, Cell, ResponsiveContainer } from "recharts";
|
||||
import { Search, ChevronDown, Users, UserCheck, UserX, Clock } from "lucide-react";
|
||||
import NavbarAdmin from "../Layout/NavbarAdmin";
|
||||
import SidebarAdmin from "../Layout/SidebarAdmin";
|
||||
|
||||
type ChartDataItem = {
|
||||
name: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
type StatCard = {
|
||||
title: string;
|
||||
value: number;
|
||||
subtitle: string;
|
||||
icon: React.ReactNode;
|
||||
iconColor: string;
|
||||
};
|
||||
|
||||
type DashboardApiResponse = {
|
||||
totalPendaftar: number;
|
||||
totalDiterima: number;
|
||||
totalDitolak: number;
|
||||
totalDiproses: number;
|
||||
universitas: ChartDataItem[];
|
||||
negara: ChartDataItem[];
|
||||
};
|
||||
|
||||
const UNIVERSITAS_COLORS = ["#001a66", "#334d99", "#6699cc", "#99b3ff"];
|
||||
const PENERIMAAN_COLORS = ["#0088ff", "#00cc00", "#ff3333"];
|
||||
const NEGARA_COLORS = ["#cc3333", "#dd6666", "#ee9999", "#f0b3b3", "#f5cccc"];
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const [statCards, setStatCards] = useState<StatCard[]>([]);
|
||||
const [universitasData, setUniversitasData] = useState<ChartDataItem[]>([]);
|
||||
const [penerimaanData, setPenerimaanData] = useState<ChartDataItem[]>([]);
|
||||
const [negaraData, setNegaraData] = useState<ChartDataItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDashboardData = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/lowongan-magang-api/get`
|
||||
);
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Gagal mengambil data dashboard:", result.message);
|
||||
setErrorMessage(result.message || "Gagal mengambil data dashboard.");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const data: DashboardApiResponse = result.data;
|
||||
|
||||
const statCardsFromApi: StatCard[] = [
|
||||
{
|
||||
title: "Total Pendaftar",
|
||||
value: data.totalPendaftar,
|
||||
subtitle: "+200 dari batch sebelumnya", // sesuaikan bila BE sudah support
|
||||
icon: <Users size={24} />,
|
||||
iconColor: "#0088ff",
|
||||
},
|
||||
{
|
||||
title: "Total Diterima",
|
||||
value: data.totalDiterima,
|
||||
subtitle: `${(
|
||||
(data.totalDiterima / (data.totalPendaftar || 1)) *
|
||||
100
|
||||
).toFixed(1)}% acceptance rate`,
|
||||
icon: <UserCheck size={24} />,
|
||||
iconColor: "#00cc00",
|
||||
},
|
||||
{
|
||||
title: "Total Ditolak",
|
||||
value: data.totalDitolak,
|
||||
subtitle: `${(
|
||||
(data.totalDitolak / (data.totalPendaftar || 1)) *
|
||||
100
|
||||
).toFixed(1)}% rejection rate`,
|
||||
icon: <UserX size={24} />,
|
||||
iconColor: "#ff3333",
|
||||
},
|
||||
{
|
||||
title: "Sedang Diproses",
|
||||
value: data.totalDiproses,
|
||||
subtitle: `${(
|
||||
(data.totalDiproses / (data.totalPendaftar || 1)) *
|
||||
100
|
||||
).toFixed(1)}% pending review`,
|
||||
icon: <Clock size={24} />,
|
||||
iconColor: "#ffaa00",
|
||||
},
|
||||
];
|
||||
|
||||
const penerimaanFromApi: ChartDataItem[] = [
|
||||
{ name: "Total Pendaftar", value: data.totalPendaftar },
|
||||
{ name: "Total Diterima", value: data.totalDiterima },
|
||||
{ name: "Total Ditolak", value: data.totalDitolak },
|
||||
];
|
||||
|
||||
setStatCards(statCardsFromApi);
|
||||
setUniversitasData(data.universitas || []);
|
||||
setPenerimaanData(penerimaanFromApi);
|
||||
setNegaraData(data.negara || []);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Terjadi kesalahan saat mengambil data dashboard:", error);
|
||||
setErrorMessage("Terjadi kesalahan saat mengambil data dashboard.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchDashboardData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full overflow-hidden">
|
||||
<NavbarAdmin />
|
||||
|
||||
<div className="flex flex-row">
|
||||
<SidebarAdmin />
|
||||
|
||||
<div className="w-full flex-col flex-wrap bg-white p-10">
|
||||
{/* Header Section */}
|
||||
<div className="mb-[50px] flex flex-row items-center justify-between">
|
||||
<h2 className="text-[24px] font-semibold">Dashboard</h2>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Batch Filter */}
|
||||
<button className="flex items-center gap-2 rounded-xl border border-gray-300 bg-white px-[20px] py-[10px] text-gray-700 hover:bg-gray-50">
|
||||
Batch 3 - 2025
|
||||
<ChevronDown size={18} />
|
||||
</button>
|
||||
|
||||
{/* Search Box */}
|
||||
<div className="flex items-center gap-2 rounded-xl border border-gray-300 bg-white px-[20px] py-[10px]">
|
||||
<Search size={18} className="text-gray-400" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nama, Jurusan,"
|
||||
className="w-52 text-sm text-gray-700 placeholder-gray-400 outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Loading / Error State */}
|
||||
{loading && (
|
||||
<p className="text-center text-gray-600">Memuat data dashboard...</p>
|
||||
)}
|
||||
|
||||
{!loading && errorMessage && (
|
||||
<p className="text-center text-red-500">{errorMessage}</p>
|
||||
)}
|
||||
|
||||
{!loading && !errorMessage && (
|
||||
<>
|
||||
{/* Stat Cards */}
|
||||
<div className="mb-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
{statCards.map((card, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="rounded-lg border border-gray-300 bg-white p-6"
|
||||
>
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
<div
|
||||
className="rounded-lg bg-gray-100 p-2"
|
||||
style={{ color: card.iconColor }}
|
||||
>
|
||||
{card.icon}
|
||||
</div>
|
||||
<h3 className="text-sm font-medium text-blue-600">
|
||||
{card.title}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<p className="text-2xl font-bold text-gray-900">
|
||||
{card.value}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs" style={{ color: card.iconColor }}>
|
||||
{card.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Charts Section */}
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
{/* Universitas Chart */}
|
||||
<div className="rounded-lg border border-gray-300 bg-white p-6">
|
||||
<h2 className="mb-6 text-lg font-bold text-gray-900">
|
||||
Universitas
|
||||
</h2>
|
||||
|
||||
<div className="mb-6 flex justify-center">
|
||||
<ResponsiveContainer width="100%" height={250}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={universitasData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={0}
|
||||
outerRadius={100}
|
||||
paddingAngle={2}
|
||||
dataKey="value"
|
||||
>
|
||||
{universitasData.map((_, index) => (
|
||||
<Cell
|
||||
key={`universitas-cell-${index}`}
|
||||
fill={
|
||||
UNIVERSITAS_COLORS[
|
||||
index % UNIVERSITAS_COLORS.length
|
||||
]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="space-y-3 text-sm">
|
||||
{universitasData.map((item, index) => (
|
||||
<div
|
||||
key={`universitas-legend-${index}`}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="h-3 w-3 rounded"
|
||||
style={{
|
||||
backgroundColor:
|
||||
UNIVERSITAS_COLORS[
|
||||
index % UNIVERSITAS_COLORS.length
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<span className="text-gray-700">{item.name}</span>
|
||||
</div>
|
||||
<span className="text-sm font-semibold text-gray-900">
|
||||
{item.value}%
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Penerimaan & Negara */}
|
||||
<div className="space-y-6">
|
||||
{/* Penerimaan Chart */}
|
||||
<div className="rounded-lg border border-gray-300 bg-white p-6">
|
||||
<h2 className="mb-6 text-lg font-bold text-gray-900">
|
||||
Penerimaan
|
||||
</h2>
|
||||
|
||||
<div className="mb-6 flex justify-center">
|
||||
<ResponsiveContainer width="100%" height={180}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={penerimaanData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={0}
|
||||
outerRadius={70}
|
||||
paddingAngle={2}
|
||||
dataKey="value"
|
||||
>
|
||||
{penerimaanData.map((_, index) => (
|
||||
<Cell
|
||||
key={`penerimaan-cell-${index}`}
|
||||
fill={
|
||||
PENERIMAAN_COLORS[
|
||||
index % PENERIMAAN_COLORS.length
|
||||
]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="space-y-2 text-sm">
|
||||
{penerimaanData.map((item, index) => (
|
||||
<div
|
||||
key={`penerimaan-legend-${index}`}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<div
|
||||
className="h-3 w-3 rounded"
|
||||
style={{
|
||||
backgroundColor:
|
||||
PENERIMAAN_COLORS[
|
||||
index % PENERIMAAN_COLORS.length
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<span className="text-gray-700">{item.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Negara Chart */}
|
||||
<div className="rounded-lg border border-gray-300 bg-white p-6">
|
||||
<h2 className="mb-6 text-lg font-bold text-gray-900">
|
||||
Negara
|
||||
</h2>
|
||||
|
||||
<div className="mb-6 flex justify-center">
|
||||
<ResponsiveContainer width="100%" height={180}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={negaraData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={0}
|
||||
outerRadius={70}
|
||||
paddingAngle={2}
|
||||
dataKey="value"
|
||||
>
|
||||
{negaraData.map((_, index) => (
|
||||
<Cell
|
||||
key={`negara-cell-${index}`}
|
||||
fill={
|
||||
NEGARA_COLORS[
|
||||
index % NEGARA_COLORS.length
|
||||
]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="space-y-2 text-sm">
|
||||
{negaraData.map((item, index) => (
|
||||
<div
|
||||
key={`negara-legend-${index}`}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<div
|
||||
className="h-3 w-3 rounded"
|
||||
style={{
|
||||
backgroundColor:
|
||||
NEGARA_COLORS[index % NEGARA_COLORS.length],
|
||||
}}
|
||||
/>
|
||||
<span className="text-gray-700">{item.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
@@ -23,8 +23,10 @@ const DetailsProduct = () => {
|
||||
fetch(`${import.meta.env.VITE_API_BASE_URL}/hasil-research-api/get/${id}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data && data.data && data.data.length > 0) {
|
||||
setProduct(data.data[0]);
|
||||
if (data && data.data) {
|
||||
setProduct(data.data);
|
||||
} else {
|
||||
setProduct(null);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
@@ -43,6 +45,8 @@ const DetailsProduct = () => {
|
||||
<div className="text-center mt-10 text-red-500">Data tidak ditemukan</div>
|
||||
);
|
||||
}
|
||||
console.log(`${import.meta.env.VITE_API_BASE_URL}${product.image_path}`);
|
||||
|
||||
|
||||
return (
|
||||
<div className="body-details-of-product bg-[#F8F9FA] min-h-screen">
|
||||
|
||||
@@ -144,9 +144,9 @@ const EditPartnerships = () => {
|
||||
<div className="flex flex-row">
|
||||
<SidebarAdmin />
|
||||
<div className="p-10 bg-white flex-col flex-wrap w-full">
|
||||
<div className="header-button flex justify-between items-center mb-[45px]">
|
||||
<div className="flex justify-between items-center mb-[45px]">
|
||||
<h2 className="text-[24px] font-semibold">Edit Partnership</h2>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex gap-4 pr-10">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSubmit}
|
||||
@@ -165,7 +165,7 @@ const EditPartnerships = () => {
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-8 w-full">
|
||||
<div className="flex flex-row gap-16">
|
||||
<div className="flex flex-row gap-10">
|
||||
<div className="flex flex-col items-start gap-[15px]">
|
||||
<h2 className="text-[16px] font-bold">Nama</h2>
|
||||
<input
|
||||
|
||||
@@ -57,7 +57,7 @@ const Internships = () => {
|
||||
title: item.posisi,
|
||||
location: item.lokasi,
|
||||
category: item.kelompok_peminatan,
|
||||
status: item.status_lowongan === "dibuka" ? "Opened" : "Closed",
|
||||
status: item.status_lowongan === "dibuka" ? "Open" : "Closed",
|
||||
statusColor:
|
||||
item.status_lowongan === "dibuka" ? "#4EAD00" : "#C3423F",
|
||||
isPaid: item.paid === "paid",
|
||||
@@ -171,12 +171,12 @@ const Internships = () => {
|
||||
<div
|
||||
key={index}
|
||||
className={`card-1 flex flex-col items-start p-5 lg:p-7 rounded-xl shadow-2xl ${
|
||||
intern.status === "Opened"
|
||||
intern.status === "Open"
|
||||
? "bg-white cursor-pointer hover:shadow-xl transition"
|
||||
: "bg-gray-200 cursor-not-allowed opacity-60"
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (intern.status === "Opened") {
|
||||
if (intern.status === "Open") {
|
||||
navigate(`/details/${intern.id}`);
|
||||
}
|
||||
}}
|
||||
@@ -189,7 +189,7 @@ const Internships = () => {
|
||||
className="p-2 border-2 text-[12px] lg:text-[14px] font-bold rounded-4xl"
|
||||
style={{
|
||||
backgroundColor:
|
||||
intern.status === "Opened" ? "#F6FFF4" : "#FAF0EF",
|
||||
intern.status === "Open" ? "#F6FFF4" : "#FAF0EF",
|
||||
color: intern.statusColor,
|
||||
borderColor: intern.statusColor,
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
import NavbarAdmin from "../Layout/NavbarAdmin";
|
||||
import SidebarAdmin from "../Layout/SidebarAdmin";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
type Faq = {
|
||||
id: number;
|
||||
pertanyaan: string;
|
||||
jawaban: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
const ListFaq = () => {
|
||||
const navigate = useNavigate();
|
||||
const [faq, setFaq] = useState<Faq[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFaq = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/faq-api/get`
|
||||
);
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
setFaq(result.data);
|
||||
} else {
|
||||
console.error("Gagal mengambil data FAQ:", result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Terjadi kesalahan:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFaq();
|
||||
}, []);
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
const confirmDelete = confirm("Apakah Anda yakin ingin menghapus FAQ ini?");
|
||||
if (!confirmDelete) return;
|
||||
|
||||
const token = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("token="))
|
||||
?.split("=")[1];
|
||||
|
||||
if (!token) {
|
||||
alert("Token tidak ditemukan. Silakan login ulang.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/faq-api/delete/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
alert("Feedback berhasil dihapus!");
|
||||
setFaq((prev) => prev.filter((faq) => faq.id !== id));
|
||||
} else {
|
||||
alert(result.message || "Gagal menghapus FAQ.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal menghapus:", error);
|
||||
alert("Terjadi kesalahan saat menghapus FAQ.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full overflow-hidden">
|
||||
<NavbarAdmin />
|
||||
|
||||
<div className="flex flex-row">
|
||||
<SidebarAdmin />
|
||||
<div className="p-10 bg-white flex-col flex-wrap w-full">
|
||||
<div className="flex flex-row justify-between items-center mb-[50px]">
|
||||
<h2 className="text-[24px] font-semibold">
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
<button
|
||||
className="px-[20px] py-[10px] rounded-xl bg-[#1F4A92] hover:bg-[#677c9f] cursor-pointer text-white font-semibold"
|
||||
onClick={() => navigate("/add-faq")}
|
||||
>
|
||||
Tambah
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr className="text-left">
|
||||
<th className="px-6 py-3">Questions</th>
|
||||
<th className="px-6 py-3">Answers</th>
|
||||
<th className="px-6 py-3">Tanggal Unggah</th>
|
||||
<th className="px-6 py-3">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{faq.map((faq) => (
|
||||
<tr key={faq.id} className="hover:bg-gray-50">
|
||||
<td className="px-6 py-3">{faq.pertanyaan}</td>
|
||||
<td className="px-6 py-3">
|
||||
<div dangerouslySetInnerHTML={{ __html: faq.jawaban }} />
|
||||
</td>
|
||||
|
||||
<td className="px-6 py-3">
|
||||
{new Date(faq.created_at).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-3 flex gap-2">
|
||||
{/* Tombol View */}
|
||||
<button
|
||||
className="bg-[#3BE21D] cursor-pointer text-white p-2 rounded-xl"
|
||||
onClick={() =>
|
||||
window.open(`/details-product/${faq.id}`, "_blank")
|
||||
}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Tombol Edit */}
|
||||
<button
|
||||
className="bg-[#2CAEFF] text-white cursor-pointer p-2 rounded-xl"
|
||||
onClick={() => navigate(`/edit-faq/${faq.id}`)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19.5 7.125 18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Tombol Hapus */}
|
||||
<button
|
||||
className="bg-[#E41E1E] text-white cursor-pointer p-2 rounded-xl"
|
||||
onClick={() => handleDelete(faq.id)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{faq.length === 0 && (
|
||||
<p className="text-gray-500 mt-4 text-center">
|
||||
Belum ada data FAQ.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListFaq;
|
||||
@@ -0,0 +1,207 @@
|
||||
import NavbarAdmin from "../Layout/NavbarAdmin";
|
||||
import SidebarAdmin from "../Layout/SidebarAdmin";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
type Feedback = {
|
||||
id: number;
|
||||
nama: string;
|
||||
image: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
const ListFeedback = () => {
|
||||
const navigate = useNavigate();
|
||||
const [feedbacks, setFeedbacks] = useState<Feedback[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFeedbacks = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/feedback-api/get`
|
||||
);
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
setFeedbacks(result.data);
|
||||
} else {
|
||||
console.error("Gagal mengambil data feedback:", result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Terjadi kesalahan:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFeedbacks();
|
||||
}, []);
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
const confirmDelete = confirm(
|
||||
"Apakah Anda yakin ingin menghapus feedback ini?"
|
||||
);
|
||||
if (!confirmDelete) return;
|
||||
|
||||
const token = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("token="))
|
||||
?.split("=")[1];
|
||||
|
||||
if (!token) {
|
||||
alert("Token tidak ditemukan. Silakan login ulang.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_BASE_URL}/feedback-api/delete/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
alert("Feedback berhasil dihapus!");
|
||||
setFeedbacks((prev) => prev.filter((feedback) => feedback.id !== id));
|
||||
} else {
|
||||
alert(result.message || "Gagal menghapus feedback.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal menghapus:", error);
|
||||
alert("Terjadi kesalahan saat menghapus feedback.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full overflow-hidden">
|
||||
<NavbarAdmin />
|
||||
|
||||
<div className="flex flex-row">
|
||||
<SidebarAdmin />
|
||||
<div className="p-10 bg-white flex-col flex-wrap w-full">
|
||||
<div className="flex flex-row justify-between items-center mb-[50px]">
|
||||
<h2 className="text-[24px] font-semibold">Feedback</h2>
|
||||
<button
|
||||
className="px-[20px] py-[10px] rounded-xl bg-[#1F4A92] hover:bg-[#677c9f] cursor-pointer text-white font-semibold"
|
||||
onClick={() => navigate("/add-feedback")}
|
||||
>
|
||||
Tambah
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr className="text-left">
|
||||
<th className="px-6 py-3">Nama</th>
|
||||
<th className="px-6 py-3">Foto</th>
|
||||
<th className="px-6 py-3">Tanggal Unggah</th>
|
||||
<th className="px-6 py-3">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{feedbacks.map((feedback) => (
|
||||
<tr key={feedback.id} className="hover:bg-gray-50">
|
||||
<td className="px-6 py-3">{feedback.nama}</td>
|
||||
<td className="px-6 py-3">
|
||||
<img src={feedback.image} alt={feedback.nama} />
|
||||
</td>
|
||||
<td className="px-6 py-3">
|
||||
{new Date(feedback.created_at).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-3 flex gap-2">
|
||||
{/* Tombol View */}
|
||||
<button
|
||||
className="bg-[#3BE21D] cursor-pointer text-white p-2 rounded-xl"
|
||||
onClick={() =>
|
||||
window.open(`/details-product/${feedback.id}`, "_blank")
|
||||
}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Tombol Edit */}
|
||||
<button
|
||||
className="bg-[#2CAEFF] text-white cursor-pointer p-2 rounded-xl"
|
||||
onClick={() =>
|
||||
navigate(`/edit-product-humic/${feedback.id}`)
|
||||
}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19.5 7.125 18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Tombol Hapus */}
|
||||
<button
|
||||
className="bg-[#E41E1E] text-white cursor-pointer p-2 rounded-xl"
|
||||
onClick={() => handleDelete(feedback.id)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{feedbacks.length === 0 && (
|
||||
<p className="text-gray-500 mt-4 text-center">Belum ada data feedback.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListFeedback;
|
||||
@@ -190,7 +190,7 @@ const ListProduct = () => {
|
||||
</table>
|
||||
|
||||
{projects.length === 0 && (
|
||||
<p className="text-gray-500 mt-4">Belum ada data project.</p>
|
||||
<p className="text-gray-500 mt-4 text-center">Belum ada data project.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,10 @@ const RegisterInternships = () => {
|
||||
nama_belakang: "",
|
||||
email: "",
|
||||
kontak: "",
|
||||
universitas: "",
|
||||
negara: "",
|
||||
jurusan: "",
|
||||
batch: "",
|
||||
motivasi: "",
|
||||
relevant_skills: "",
|
||||
cv: null as File | null,
|
||||
@@ -44,7 +47,10 @@ const RegisterInternships = () => {
|
||||
}
|
||||
data.append("email", formData.email);
|
||||
data.append("kontak", formData.kontak);
|
||||
data.append("universitas", formData.universitas);
|
||||
data.append("negara", formData.negara);
|
||||
data.append("jurusan", formData.jurusan);
|
||||
data.append("batch", formData.batch);
|
||||
data.append("motivasi", formData.motivasi);
|
||||
data.append("relevant_skills", formData.relevant_skills);
|
||||
data.append("g-recaptcha-response", recaptchaToken);
|
||||
@@ -131,12 +137,13 @@ const RegisterInternships = () => {
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border-2 border-[#A9B5C0] rounded-md p-2 mt-3 text-black"
|
||||
placeholder="John"
|
||||
placeholder="Muhammad Budi"
|
||||
value={formData.nama_depan}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, nama_depan: e.target.value })
|
||||
}
|
||||
/>
|
||||
<p className="text-sm text-[#C3423F] mt-1">*required</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -144,13 +151,13 @@ const RegisterInternships = () => {
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border-2 border-[#A9B5C0] rounded-md p-2 mt-3 text-black"
|
||||
placeholder="Doe"
|
||||
placeholder="Pekerti"
|
||||
value={formData.nama_belakang}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, nama_belakang: e.target.value })
|
||||
}
|
||||
/>
|
||||
<p className="text-sm text-[#C3423F] mt-1">*not required</p>
|
||||
<p className="text-sm text-[#C3423F] mt-1">*required</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -179,6 +186,32 @@ const RegisterInternships = () => {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1 font-bold">Universitas</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border-2 border-[#A9B5C0] rounded-md p-2 mt-3 text-black"
|
||||
placeholder="Telkom University Bandung"
|
||||
value={formData.universitas}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, universitas: e.target.value })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1 font-bold">Negara</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border-2 border-[#A9B5C0] rounded-md p-2 mt-3 text-black"
|
||||
placeholder="Indonesia"
|
||||
value={formData.negara}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, negara: e.target.value })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1 font-bold">Jurusan</label>
|
||||
@@ -207,6 +240,19 @@ const RegisterInternships = () => {
|
||||
<p className="text-sm text-[#C3423F] mt-1">*separate with commas</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1 font-bold">Batch</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border-2 border-[#A9B5C0] rounded-md p-2 mt-3 text-black"
|
||||
placeholder="1"
|
||||
value={formData.batch}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, batch: e.target.value })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block mb-1 font-bold">
|
||||
CV (Curriculum Vitae)
|
||||
|
||||
Reference in New Issue
Block a user