diff --git a/src/FollowUps.module.css b/src/FollowUps.module.css
index 348a108..8b88130 100644
--- a/src/FollowUps.module.css
+++ b/src/FollowUps.module.css
@@ -1,5 +1,4 @@
.container {
- padding: 24px;
background-color: #f7f9fa;
font-family: 'Amazon Ember', sans-serif;
}
diff --git a/src/ProfileTab.js b/src/ProfileTab.js
index bfcfec2..1ba46fc 100644
--- a/src/ProfileTab.js
+++ b/src/ProfileTab.js
@@ -1,43 +1,41 @@
import React, { useState, useRef, useEffect } from 'react';
-import { FaPen } from 'react-icons/fa';
-import styles from './ProfileTab.module.css';
import { useNavigate } from 'react-router-dom';
+import styles from './ProfileTab.module.css';
const ProfileTab = () => {
const menuRef = useRef(null);
const navigate = useNavigate();
const [isEditing, setIsEditing] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const [profile, setProfile] = useState({});
+ const [profileTemp, setProfileTemp] = useState({});
+
+ const licenses = [
+ { id: 1, type: "Current Subscription", number: "DRML-2025-AI001", validUntil: "June 30 2025" },
+ ];
- // Close dropdown if click outside
useEffect(() => {
const handleClickOutside = (event) => {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setIsMenuOpen(false);
}
};
-
document.addEventListener('mousedown', handleClickOutside);
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
+ return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const handleLogout = () => {
localStorage.removeItem('token');
localStorage.removeItem('user');
- navigator.serviceWorker.ready.then(function (registration) {
- registration.pushManager.getSubscription().then(function (subscription) {
+ navigator.serviceWorker.ready.then((registration) => {
+ registration.pushManager.getSubscription().then((subscription) => {
if (subscription) {
- subscription.unsubscribe().then(function (successful) {
- console.log('Push subscription unsubscribed on logout:', successful);
- // Optional: also notify backend to clear the token
+ subscription.unsubscribe().then((successful) => {
+ console.log('Unsubscribed from push notifications:', successful);
fetch('/api/clear-subscription', {
method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
+ headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ endpoint: subscription.endpoint }),
});
});
@@ -48,73 +46,79 @@ const ProfileTab = () => {
window.location.reload();
};
- useEffect(() => {
- const fetchData = async () => {
- const token = localStorage.getItem('token');
+ useEffect(() => {
+ const fetchData = async () => {
+ const token = localStorage.getItem('token');
- try {
- const response = await fetch('https://bot.kediritechnopark.com/webhook/dashboard?profileOnly=true', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${token}`,
- },
- });
+ try {
+ const response = await fetch('https://bot.kediritechnopark.com/webhook/dashboard?profileOnly=true', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ },
+ });
- if (response.status === 401 || response.status === 403) {
- handleLogout();
- return;
- }
+ if (response.status === 401 || response.status === 403) {
+ handleLogout();
+ return;
+ }
- if (!response.ok) {
- throw new Error('Fetch gagal dengan status: ' + response.status);
- }
+ if (!response.ok) {
+ throw new Error('Fetch gagal dengan status: ' + response.status);
+ }
- const data = await response.json();
- console.log(data);
+ const data = await response.json();
+ setProfile(data.profile_data);
+ setProfileTemp(data.profile_data);
+ } catch (error) {
+ console.error('Fetch error:', error);
+ navigate('/login');
+ }
+ };
- setProfile(data.profile_data);
- } catch (error) {
- console.error('Error:', error);
- navigate('/login');
- }
- };
-
- fetchData(); // Jalankan langsung saat komponen di-mount
-
- }, [navigate]);
-
- const [profile, setProfile] = useState({});
-
- const licenses = [
- { id: 1, type: "AI Bot License", number: "DL-2025-AI001", validUntil: "2026-12-31" },
- { id: 2, type: "Clinic Data Access", number: "DL-2025-CL002", validUntil: "2026-06-30" }
- ];
+ fetchData();
+ }, [navigate]);
const handleChange = (e) => {
const { name, value } = e.target;
- setProfile((prev) => ({ ...prev, [name]: value }));
+ setProfile(prev => ({ ...prev, [name]: value }));
};
+
const handleSave = async () => {
try {
const token = localStorage.getItem('token');
+ if (profile.newPassword && profile.newPassword !== profile.confirmPassword) {
+ alert('Password dan konfirmasi tidak sama.');
+ return;
+ }
+
+ const payload = { ...profile };
+ if (!payload.newPassword) {
+ delete payload.newPassword;
+ delete payload.confirmPassword;
+ } else {
+ payload.password = payload.newPassword;
+ delete payload.newPassword;
+ delete payload.confirmPassword;
+ }
+
const response = await fetch('https://bot.kediritechnopark.com/webhook/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
- body: JSON.stringify(profile),
+ body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('Gagal menyimpan profil');
const result = await response.json();
- console.log('Profil berhasil diperbarui:', result);
setIsEditing(false);
- alert('Profil berhasil disimpan!');
+ alert('Profile saved!');
} catch (error) {
console.error('Error saat menyimpan profil:', error);
alert('Terjadi kesalahan saat menyimpan profil.');
@@ -123,8 +127,7 @@ const ProfileTab = () => {
return (
-
-
Profil Perusahaan
+
)}
+

+
+
Dermalounge AI Admin Profile
+
-
{["name", "company", "address", "email", "phone"].map((field) => (
@@ -164,35 +161,75 @@ const ProfileTab = () => {
-
))}
- {isEditing &&
-
+
+
+
+
+
+
+
+
+ >
+ )}
+
+ {!isEditing &&
+
setIsEditing(true)}
>
- Simpan
+ Edit
+
+ }
+ {isEditing &&
+
+
+ {/*
{
+ setIsEditing(false);
+ setProfile(profileTemp);
+ }}>
+ Batal
+
*/}
+
+ Save
+
}
-
License
{licenses.map((item) => (
{item.type}
-
No: {item.number}
-
Berlaku sampai: {item.validUntil}
+
{item.number}
+
Free License Valid until: {item.validUntil}
))}
+
+
+ © 2025 Kediri Technopark
+
);
};
diff --git a/src/ProfileTab.module.css b/src/ProfileTab.module.css
index b14a068..75c14f2 100644
--- a/src/ProfileTab.module.css
+++ b/src/ProfileTab.module.css
@@ -1,40 +1,31 @@
-/* Container */
+/* Container Utama */
.dashboardContainer {
- width: 100%;
+ max-width: 900px;
+ margin: 30px auto;
+ background: #fff;
+ border-radius: 10px;
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
padding: 20px;
- max-width: 1200px;
- margin: 0 auto;
- box-sizing: border-box;
}
-/* Header */
-.profileHeader {
+.dashboardHeader {
display: flex;
- justify-content: space-between;
align-items: center;
- flex-wrap: wrap;
- margin-bottom: 20px;
- gap: 12px;
-}
-
-.editButton {
- background-color: #075e54;
+ gap: 15px;
+ background: #075e54;
color: white;
- padding: 6px 12px;
- font-size: 12px;
- border: none;
- border-radius: 8px;
- display: flex;
- align-items: center;
- gap: 6px;
- cursor: pointer;
- transition: background-color 0.3s ease;
+ padding: 20px;
+ border-radius: 10px 10px 0 0;
+ position: relative;
}
-.editButton:hover {
- background-color: #0f9b8a;
+.dashboardHeader img {
+ width: 75px;
+ height: 75px;
+ border-radius: 50%;
}
+
/* Profile Section */
.profileSection {
display: flex;
@@ -43,6 +34,7 @@
gap: 20px;
flex-wrap: wrap;
margin-bottom: 30px;
+ margin-top: 20px;
}
.companyImage {
@@ -80,64 +72,42 @@
color: white;
padding: 16px;
border-radius: 12px;
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
font-size: 14px;
}
-/* ====================
- Responsive Breakpoints
-==================== */
-
-/* Tablet (768px – 1023px) */
-@media screen and (max-width: 1023px) {
- .profileSection {
- flex-direction: row;
- justify-content: flex-start;
- }
-
- .profileDetails {
- min-width: 100%;
- }
-
- .licenseCards {
- grid-template-columns: repeat(2, 1fr);
- }
+.profileHeader {
+ flex-direction: column;
+ align-items: flex-start;
}
-/* Mobile (≤ 767px) */
-@media screen and (max-width: 767px) {
- .profileHeader {
- flex-direction: column;
- align-items: flex-start;
- }
-
- .editButton {
- align-self: flex-end;
- }
-
- .profileSection {
- flex-direction: column;
- align-items: center;
- text-align: center;
- }
-
- .companyImage {
- margin-bottom: 12px;
- }
-
- .profileDetails {
- text-align: left;
- width: 100%;
- }
-
- .licenseCards {
- grid-template-columns: 1fr;
- }
-
- .licenseCard {
- font-size: 13px;
- }
+.editButton {
+ align-self: flex-end;
}
+
+.profileSection {
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+}
+
+.companyImage {
+ margin-bottom: 12px;
+}
+
+.profileDetails {
+ text-align: left;
+ width: 100%;
+}
+
+.licenseCards {
+ grid-template-columns: 1fr;
+}
+
+.licenseCard {
+ font-size: 13px;
+}
+
.profileInputGroup {
display: flex;
align-items: center;
@@ -147,7 +117,8 @@
}
.profileInputGroup label {
- min-width: 100px; /* atau sesuai label terpanjang */
+ min-width: 110px;
+ /* atau sesuai label terpanjang */
font-size: 14px;
}
@@ -162,7 +133,8 @@
width: auto;
max-width: 100%;
- flex: 1; /* biar input bisa melar jika ruang tersedia */
+ flex: 1;
+ /* biar input bisa melar jika ruang tersedia */
}
/* Saat tidak dalam mode editing */
@@ -173,18 +145,17 @@
color: #000;
}
-
.dropdownContainer {
position: relative;
display: inline-block;
position: absolute;
- top: 37px;
- right: 10px;
+ bottom: 5px;
+ right: 5px;
}
.dropdownToggle {
- color: #ffff;
- background-color: #255e54;
+ background-color: #ffff;
+ color: #255e54;
padding: 8px 12px;
border: none;
border-radius: 4px;
@@ -216,3 +187,62 @@
background-color: #f0f0f0;
}
+
+/* Input Profil */
+.profileForm {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ padding: 20px 0;
+}
+
+.editableInput {
+ font-size: 14px;
+ padding: 8px 12px;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ background-color: #f9f9f9;
+ color: #333;
+ transition: border-color 0.3s;
+ flex: 1;
+}
+
+.readOnly {
+ border-color: transparent;
+ background-color: transparent;
+ pointer-events: none;
+ color: #000;
+}
+
+/* Footer (optional) */
+.footer {
+ text-align: center;
+ margin-top: 30px;
+ font-size: 13px;
+ color: #777;
+}
+
+
+/* Mobile styles */
+@media (max-width: 768px) {
+.h1 {
+ color: white;
+ font-size: 23px;
+}
+.dashboardContainer {
+ max-width: 900px;
+ margin: 30px auto;
+ background: #fff;
+ border-radius: 10px;
+ box-shadow: none;
+ padding: 20px;
+}
+
+ .desktopText {
+ display: none;
+ }
+
+ .mobileText {
+ display: block;
+ }
+}
\ No newline at end of file