Update API Keys and fix frontend bugs
This commit is contained in:
@@ -132,6 +132,9 @@ exports.analyzeImage = async (req, res) => {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error analyzing image:', error.response ? JSON.stringify(error.response.data) : error.message);
|
console.error('Error analyzing image:', error.response ? JSON.stringify(error.response.data) : error.message);
|
||||||
res.status(500).json({ error: error.message || 'Failed to analyze image' });
|
res.status(500).json({
|
||||||
|
error: error.message || 'Failed to analyze image',
|
||||||
|
details: error.response ? error.response.data : null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ dotenv.config({ path: path.join(__dirname, '../.env') });
|
|||||||
const Config = require('../models/Config');
|
const Config = require('../models/Config');
|
||||||
const { encrypt } = require('../utils/crypto');
|
const { encrypt } = require('../utils/crypto');
|
||||||
|
|
||||||
const PERFECT_CORP_API_KEY = 'sk-tHtPU8rst9cifrM6RhkjCKy9EpNfXBdcoCbzP0QDRhIkHr_InBYPS6g_6XGSumbm';
|
const PERFECT_CORP_API_KEY = 'sk-cKf0TV79X6Fp3CZwKfsnHv1Rq9GOK6nFCAXyDG9CvnIFKGYiTtSdOnjyadBaYc5y';
|
||||||
const PERFECT_CORP_SECRET_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCKexD/nyLrGq7jXCxYi/Tjwr297cZS5EUFQbCV9NO2XQU/sJitsT2WBHHS8AYAQrzM56hPgnwpt/B+EuSuLfVbM/ympBUvSrULUNlrJF5lW96JMd1KF8RWC9fICZJuCE2iGpGAd0l9pOCUmYM4JZ/n26HxZxSSramkoOYxmhFYhQIDAQAB';
|
const PERFECT_CORP_SECRET_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAiLqa5kyq7FR9WmK++HCPNjGLsljLfvJQq+JZzCx7pfLlVD5KzRb17iglHQJkFkSHxfqxtYNwosA54oT9uD1kA96TJy+uOLRwxry7BMPTFQyDdIoloJCI/Bl+SEnSxjDoYpU8zA3S4GDkmLH3A7vHkxhMydgB8B1EOxJahQsF7QIDAQAB';
|
||||||
|
|
||||||
async function seedKeys() {
|
async function seedKeys() {
|
||||||
try {
|
try {
|
||||||
@@ -38,3 +38,10 @@ async function seedKeys() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = seedKeys;
|
module.exports = seedKeys;
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
seedKeys().then(() => {
|
||||||
|
console.log('Seeding complete. Exiting.');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const API_KEY = 'sk-cKf0TV79X6Fp3CZwKfsnHv1Rq9GOK6nFCAXyDG9CvnIFKGYiTtSdOnjyadBaYc5y';
|
||||||
|
const baseUrl = 'https://yce-api-01.makeupar.com/s2s/v2.0';
|
||||||
|
const initPayload = {
|
||||||
|
files: [
|
||||||
|
{ content_type: 'image/jpeg', file_name: 'test.jpg', file_size: 100000 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
axios.post(`${baseUrl}/file`, initPayload, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${API_KEY}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}).then(res => console.log('Init success:', JSON.stringify(res.data)))
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Init failed:', err.response ? err.response.status : err.message);
|
||||||
|
if(err.response) console.error(JSON.stringify(err.response.data));
|
||||||
|
});
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const API_KEY = 'sk-cKf0TV79X6Fp3CZwKfsnHv1Rq9GOK6nFCAXyDG9CvnIFKGYiTtSdOnjyadBaYc5y';
|
||||||
|
const baseUrl = 'https://yce-api-01.makeupar.com/s2s/v2.0';
|
||||||
|
|
||||||
|
async function test() {
|
||||||
|
try {
|
||||||
|
const initPayload = {
|
||||||
|
files: [
|
||||||
|
{ content_type: 'image/jpeg', file_name: 'test.jpg', file_size: 100000 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Init...');
|
||||||
|
const res = await axios.post(`${baseUrl}/file`, initPayload, {
|
||||||
|
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileId = res.data.data.files[0].file_id;
|
||||||
|
console.log('File ID:', fileId);
|
||||||
|
|
||||||
|
console.log('Creating task...');
|
||||||
|
const taskPayload = {
|
||||||
|
src_file_id: fileId,
|
||||||
|
dst_actions: [
|
||||||
|
"wrinkle", "pore", "texture", "acne", "moisture", "eye_bag",
|
||||||
|
"firmness", "age_spot", "radiance", "redness", "oiliness"
|
||||||
|
],
|
||||||
|
miniserver_args: {
|
||||||
|
enable_mask_overlay: false
|
||||||
|
},
|
||||||
|
format: "json"
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskRes = await axios.post(`${baseUrl}/task/skin-analysis`, taskPayload, {
|
||||||
|
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Task success:', taskRes.data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed:', err.response ? err.response.status : err.message);
|
||||||
|
if(err.response) console.error(JSON.stringify(err.response.data, null, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test();
|
||||||
+1038
-1038
File diff suppressed because it is too large
Load Diff
+340
-338
@@ -1,338 +1,340 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import API_BASE_URL from './config';
|
import API_BASE_URL from './config';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { FiSearch, FiSettings, FiHelpCircle, FiChevronLeft, FiChevronRight, FiLogOut, FiArrowLeft, FiArrowRight } from 'react-icons/fi';
|
import { FiSearch, FiSettings, FiHelpCircle, FiChevronLeft, FiChevronRight, FiLogOut, FiArrowLeft, FiArrowRight } from 'react-icons/fi';
|
||||||
import { HiOutlineDocumentText, HiOutlineUserGroup } from 'react-icons/hi';
|
import { HiOutlineDocumentText, HiOutlineUserGroup } from 'react-icons/hi';
|
||||||
|
|
||||||
const DashboardPage = () => {
|
const DashboardPage = () => {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUsers = async () => {
|
const fetchUsers = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE_URL}/api/dashboard/users`);
|
const res = await fetch(`${API_BASE_URL}/api/dashboard/users`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setUsers(data);
|
setUsers(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to fetch users", err);
|
console.error("Failed to fetch users", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchUsers();
|
fetchUsers();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
// Here you would clear any persisted auth state if it existed
|
// Here you would clear any persisted auth state if it existed
|
||||||
navigate('/admin');
|
navigate('/admin');
|
||||||
};
|
};
|
||||||
const [showSupportModal, setShowSupportModal] = useState(false);
|
const [showSupportModal, setShowSupportModal] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState('reports');
|
const [activeTab, setActiveTab] = useState('reports');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const ITEMS_PER_PAGE = 8;
|
const ITEMS_PER_PAGE = 8;
|
||||||
|
|
||||||
// Settings state
|
// Settings state
|
||||||
const [settings, setSettings] = useState({
|
const [settings, setSettings] = useState({
|
||||||
siteName: 'Limelite Face Analyzer',
|
siteName: 'Limelite Face Analyzer',
|
||||||
adminEmail: 'olivia@limelite.com',
|
adminEmail: 'olivia@limelite.com',
|
||||||
whatsappNotifications: true,
|
whatsappNotifications: true,
|
||||||
autoArchive: false
|
autoArchive: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Load users from backend
|
// Load users from backend
|
||||||
const reports = users.map(user => ({
|
const reports = users.map(user => ({
|
||||||
id: user.phone,
|
id: user.phone,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
phone: user.phone,
|
phone: user.phone,
|
||||||
report: 'View Link',
|
report: 'View Link',
|
||||||
link: user.lastLink
|
link: user.lastLink
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const filteredReports = reports.filter(report =>
|
const filteredReports = reports.filter(report =>
|
||||||
report.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
report.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
report.phone.includes(searchTerm)
|
report.phone.includes(searchTerm)
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalPages = Math.ceil(filteredReports.length / ITEMS_PER_PAGE);
|
const totalPages = Math.ceil(filteredReports.length / ITEMS_PER_PAGE);
|
||||||
const paginatedReports = filteredReports.slice(
|
const paginatedReports = filteredReports.slice(
|
||||||
(currentPage - 1) * ITEMS_PER_PAGE,
|
(currentPage - 1) * ITEMS_PER_PAGE,
|
||||||
currentPage * ITEMS_PER_PAGE
|
currentPage * ITEMS_PER_PAGE
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePageChange = (newPage) => {
|
const handlePageChange = (newPage) => {
|
||||||
if (newPage >= 1 && newPage <= totalPages) {
|
if (newPage >= 1 && newPage <= totalPages) {
|
||||||
setCurrentPage(newPage);
|
setCurrentPage(newPage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPageNumbers = () => {
|
const renderPageNumbers = () => {
|
||||||
const pages = [];
|
const pages = [];
|
||||||
if (totalPages <= 7) {
|
if (totalPages <= 7) {
|
||||||
for (let i = 1; i <= totalPages; i++) pages.push(i);
|
for (let i = 1; i <= totalPages; i++) pages.push(i);
|
||||||
} else {
|
} else {
|
||||||
if (currentPage <= 4) {
|
if (currentPage <= 4) {
|
||||||
pages.push(1, 2, 3, 4, 5, '...', totalPages);
|
pages.push(1, 2, 3, 4, 5, '...', totalPages);
|
||||||
} else if (currentPage >= totalPages - 3) {
|
} else if (currentPage >= totalPages - 3) {
|
||||||
pages.push(1, '...', totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages);
|
pages.push(1, '...', totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages);
|
||||||
} else {
|
} else {
|
||||||
pages.push(1, '...', currentPage - 1, currentPage, currentPage + 1, '...', totalPages);
|
pages.push(1, '...', currentPage - 1, currentPage, currentPage + 1, '...', totalPages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pages.map((page, index) => (
|
return pages.map((page, index) => (
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => typeof page === 'number' && handlePageChange(page)}
|
onClick={() => typeof page === 'number' && handlePageChange(page)}
|
||||||
className={`min-w-[40px] h-10 flex items-center justify-center text-sm font-semibold transition-all duration-200 rounded-lg ${currentPage === page
|
className={`min-w-[40px] h-10 flex items-center justify-center text-sm font-semibold transition-all duration-200 rounded-lg ${currentPage === page
|
||||||
? 'bg-gray-50 text-gray-900'
|
? 'bg-gray-50 text-gray-900'
|
||||||
: typeof page === 'number'
|
: typeof page === 'number'
|
||||||
? 'text-gray-500 hover:text-gray-900 hover:bg-gray-50'
|
? 'text-gray-500 hover:text-gray-900 hover:bg-gray-50'
|
||||||
: 'text-gray-400 cursor-default'
|
: 'text-gray-400 cursor-default'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{page}
|
{page}
|
||||||
</button>
|
</button>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen bg-[#F8F9FA] font-sans">
|
<div className="flex h-screen bg-[#F8F9FA] font-sans">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className="w-64 bg-limelite-red text-white flex flex-col shrink-0">
|
<aside className="w-64 bg-limelite-red text-white flex flex-col shrink-0">
|
||||||
<div className="p-6 mb-4">
|
<div className="p-6 mb-4">
|
||||||
<img src={process.env.PUBLIC_URL + '/assets/images/logo.jpg'} alt="LIMELITE" className="h-10 w-auto brightness-0 invert" />
|
<img src={process.env.PUBLIC_URL + '/assets/images/logo.jpg'} alt="LIMELITE" className="h-10 w-auto brightness-0 invert" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="flex-1 px-0 mt-4">
|
<nav className="flex-1 px-0 mt-4">
|
||||||
<div
|
<div
|
||||||
onClick={() => setActiveTab('reports')}
|
onClick={() => setActiveTab('reports')}
|
||||||
className={`flex items-center gap-3 px-6 py-4 cursor-pointer transition-colors ${activeTab === 'reports' ? 'bg-white text-limelite-red' : 'text-white hover:bg-white/10'}`}
|
className={`flex items-center gap-3 px-6 py-4 cursor-pointer transition-colors ${activeTab === 'reports' ? 'bg-white text-limelite-red' : 'text-white hover:bg-white/10'}`}
|
||||||
>
|
>
|
||||||
<HiOutlineDocumentText className="text-xl" />
|
<HiOutlineDocumentText className="text-xl" />
|
||||||
<span className="font-bold uppercase tracking-wider text-xs">Reports</span>
|
<span className="font-bold uppercase tracking-wider text-xs">Reports</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
onClick={() => setShowSupportModal(true)}
|
onClick={() => setShowSupportModal(true)}
|
||||||
className="text-white hover:bg-white/10 flex items-center gap-3 px-6 py-4 transition-colors cursor-pointer"
|
className="text-white hover:bg-white/10 flex items-center gap-3 px-6 py-4 transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
<FiHelpCircle className="text-xl" />
|
<FiHelpCircle className="text-xl" />
|
||||||
<span className="font-bold uppercase tracking-wider text-xs">Support</span>
|
<span className="font-bold uppercase tracking-wider text-xs">Support</span>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="px-0 space-y-0 mb-6 border-t border-white/10">
|
<div className="px-0 space-y-0 mb-6 border-t border-white/10">
|
||||||
<div
|
<div
|
||||||
onClick={() => setActiveTab('settings')}
|
onClick={() => setActiveTab('settings')}
|
||||||
className={`flex items-center gap-3 px-6 py-4 cursor-pointer transition-colors ${activeTab === 'settings' ? 'bg-white text-limelite-red' : 'text-white hover:bg-white/10'}`}
|
className={`flex items-center gap-3 px-6 py-4 cursor-pointer transition-colors ${activeTab === 'settings' ? 'bg-white text-limelite-red' : 'text-white hover:bg-white/10'}`}
|
||||||
>
|
>
|
||||||
<FiSettings className="text-xl" />
|
<FiSettings className="text-xl" />
|
||||||
<span className="font-bold uppercase tracking-wider text-xs">Settings</span>
|
<span className="font-bold uppercase tracking-wider text-xs">Settings</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="hover:bg-white/10 text-white flex items-center gap-3 px-6 py-5 transition-colors cursor-pointer border-t border-white/10">
|
<div className="hover:bg-white/10 text-white flex items-center gap-3 px-6 py-5 transition-colors cursor-pointer border-t border-white/10">
|
||||||
<div className="flex items-center gap-3 w-full">
|
<div className="flex items-center gap-3 w-full">
|
||||||
<div className="w-10 h-10 bg-limelite-maroon rounded-full flex items-center justify-center overflow-hidden border border-white/30 text-white font-bold text-sm">
|
<div className="w-10 h-10 bg-limelite-maroon rounded-full flex items-center justify-center overflow-hidden border border-white/30 text-white font-bold text-sm">
|
||||||
LA
|
LA
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-[13px] font-bold truncate">Limelite Admin</p>
|
<p className="text-[13px] font-bold truncate">Limelite Admin</p>
|
||||||
<p className="text-[10px] text-white/70 truncate tracking-wide">8778913301</p>
|
<p className="text-[10px] text-white/70 truncate tracking-wide">8778913301</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
onClick={handleLogout}
|
||||||
className="text-lg opacity-70 hover:opacity-100 transition-opacity p-1 ml-auto"
|
className="text-lg opacity-70 hover:opacity-100 transition-opacity p-1 ml-auto"
|
||||||
title="Logout"
|
title="Logout"
|
||||||
>
|
>
|
||||||
<FiLogOut />
|
<FiLogOut />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
<main className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="h-16 bg-white border-b flex items-center justify-between px-8 shrink-0">
|
<header className="h-16 bg-white border-b flex items-center justify-between px-8 shrink-0">
|
||||||
<h1 className="text-xl font-bold text-gray-800">
|
<h1 className="text-xl font-bold text-gray-800">
|
||||||
{activeTab === 'reports' ? 'Welcome back, Admin' : 'System Settings'}
|
{activeTab === 'reports' ? 'Welcome back, Admin' : 'System Settings'}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{activeTab === 'reports' && (
|
{activeTab === 'reports' && (
|
||||||
<div className="relative w-80">
|
<div className="relative w-80">
|
||||||
<FiSearch className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
<FiSearch className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-10 pr-4 focus:outline-none focus:ring-1 focus:ring-limelite-red text-sm text-gray-900"
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-10 pr-4 focus:outline-none focus:ring-1 focus:ring-limelite-red text-sm text-gray-900"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Content Area */}
|
{/* Content Area */}
|
||||||
<div className="flex-1 overflow-auto p-8">
|
<div className="flex-1 overflow-auto p-8">
|
||||||
{activeTab === 'reports' ? (
|
{activeTab === 'reports' ? (
|
||||||
<div className="bg-white border border-gray-200 shadow-sm overflow-hidden flex flex-col min-h-[600px]">
|
<div className="bg-white border border-gray-200 shadow-sm overflow-hidden flex flex-col min-h-[600px]">
|
||||||
<div className="p-6 border-b flex items-center justify-between">
|
<div className="p-6 border-b flex items-center justify-between">
|
||||||
<h2 className="text-lg font-bold text-gray-800">Reports</h2>
|
<h2 className="text-lg font-bold text-gray-800">Reports</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className="flex-1 overflow-x-auto">
|
<div className="flex-1 overflow-x-auto">
|
||||||
<table className="w-full text-left">
|
<table className="w-full text-left">
|
||||||
<thead className="bg-[#F9FAFB] border-b border-gray-200">
|
<thead className="bg-[#F9FAFB] border-b border-gray-200">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider w-16">S.No</th>
|
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider w-16">S.No</th>
|
||||||
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Name</th>
|
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Name</th>
|
||||||
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Mobile Number</th>
|
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Mobile Number</th>
|
||||||
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Report</th>
|
<th className="px-6 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Report</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-200">
|
<tbody className="divide-y divide-gray-200">
|
||||||
{paginatedReports.map((report, index) => (
|
{paginatedReports.map((report, index) => (
|
||||||
<tr key={report.id} className="hover:bg-gray-50 transition-colors">
|
<tr key={report.id} className="hover:bg-gray-50 transition-colors">
|
||||||
<td className="px-6 py-4 text-sm text-gray-500 font-medium">{(currentPage - 1) * ITEMS_PER_PAGE + index + 1}</td>
|
<td className="px-6 py-4 text-sm text-gray-500 font-medium">{(currentPage - 1) * ITEMS_PER_PAGE + index + 1}</td>
|
||||||
<td className="px-6 py-4 text-sm font-medium text-gray-700">{report.name}</td>
|
<td className="px-6 py-4 text-sm font-medium text-gray-700">{report.name}</td>
|
||||||
<td className="px-6 py-4 text-sm text-gray-500">{report.phone}</td>
|
<td className="px-6 py-4 text-sm text-gray-500">{report.phone}</td>
|
||||||
<td className="px-6 py-4 text-sm text-limelite-red font-semibold truncate max-w-[200px]">
|
<td className="px-6 py-4 text-sm text-limelite-red font-semibold truncate max-w-[200px]">
|
||||||
<a href={report.link} target="_blank" rel="noopener noreferrer" className="hover:underline">
|
{report.link ? (
|
||||||
{report.link ? report.link.substring(report.link.indexOf('result')) : '-'}
|
<a href={report.link.startsWith('http') ? report.link : `${API_BASE_URL}${report.link}`} target="_blank" rel="noopener noreferrer" className="hover:underline">
|
||||||
</a>
|
View Report
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
) : '-'}
|
||||||
))}
|
</td>
|
||||||
</tbody>
|
</tr>
|
||||||
</table>
|
))}
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
{/* Pagination */}
|
</div>
|
||||||
<div className="p-6 border-t flex items-center justify-between bg-white shrink-0">
|
|
||||||
<button
|
{/* Pagination */}
|
||||||
onClick={() => handlePageChange(currentPage - 1)}
|
<div className="p-6 border-t flex items-center justify-between bg-white shrink-0">
|
||||||
disabled={currentPage === 1}
|
<button
|
||||||
className="flex items-center gap-2 px-4 py-2 border border-gray-200 rounded-lg text-sm font-bold text-black hover:bg-gray-50 transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
|
onClick={() => handlePageChange(currentPage - 1)}
|
||||||
>
|
disabled={currentPage === 1}
|
||||||
<FiArrowLeft className="text-lg" />
|
className="flex items-center gap-2 px-4 py-2 border border-gray-200 rounded-lg text-sm font-bold text-black hover:bg-gray-50 transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
|
||||||
Previous
|
>
|
||||||
</button>
|
<FiArrowLeft className="text-lg" />
|
||||||
|
Previous
|
||||||
<div className="flex items-center gap-1">
|
</button>
|
||||||
{renderPageNumbers()}
|
|
||||||
</div>
|
<div className="flex items-center gap-1">
|
||||||
|
{renderPageNumbers()}
|
||||||
<button
|
</div>
|
||||||
onClick={() => handlePageChange(currentPage + 1)}
|
|
||||||
disabled={currentPage === totalPages || totalPages === 0}
|
<button
|
||||||
className="flex items-center gap-2 px-4 py-2 border border-gray-200 rounded-lg text-sm font-bold text-black hover:bg-gray-50 transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
|
onClick={() => handlePageChange(currentPage + 1)}
|
||||||
>
|
disabled={currentPage === totalPages || totalPages === 0}
|
||||||
Next
|
className="flex items-center gap-2 px-4 py-2 border border-gray-200 rounded-lg text-sm font-bold text-black hover:bg-gray-50 transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
|
||||||
<FiArrowRight className="text-lg" />
|
>
|
||||||
</button>
|
Next
|
||||||
</div>
|
<FiArrowRight className="text-lg" />
|
||||||
</div>
|
</button>
|
||||||
) : (
|
</div>
|
||||||
<div className="bg-white border border-gray-200 shadow-sm p-8 max-w-2xl">
|
</div>
|
||||||
<h2 className="text-xl font-bold text-gray-800 mb-8 pb-4 border-b">General Settings</h2>
|
) : (
|
||||||
|
<div className="bg-white border border-gray-200 shadow-sm p-8 max-w-2xl">
|
||||||
<div className="space-y-8">
|
<h2 className="text-xl font-bold text-gray-800 mb-8 pb-4 border-b">General Settings</h2>
|
||||||
<div className="grid grid-cols-3 items-center gap-4">
|
|
||||||
<label className="text-sm font-bold text-gray-700 uppercase">Site Name</label>
|
<div className="space-y-8">
|
||||||
<input
|
<div className="grid grid-cols-3 items-center gap-4">
|
||||||
type="text"
|
<label className="text-sm font-bold text-gray-700 uppercase">Site Name</label>
|
||||||
className="col-span-2 border-b-2 py-2 focus:outline-none focus:border-limelite-red transition-colors"
|
<input
|
||||||
value={settings.siteName}
|
type="text"
|
||||||
onChange={(e) => setSettings({ ...settings, siteName: e.target.value })}
|
className="col-span-2 border-b-2 py-2 focus:outline-none focus:border-limelite-red transition-colors"
|
||||||
/>
|
value={settings.siteName}
|
||||||
</div>
|
onChange={(e) => setSettings({ ...settings, siteName: e.target.value })}
|
||||||
|
/>
|
||||||
<div className="grid grid-cols-3 items-center gap-4">
|
</div>
|
||||||
<label className="text-sm font-bold text-gray-700 uppercase">Admin Email</label>
|
|
||||||
<input
|
<div className="grid grid-cols-3 items-center gap-4">
|
||||||
type="email"
|
<label className="text-sm font-bold text-gray-700 uppercase">Admin Email</label>
|
||||||
className="col-span-2 border-b-2 py-2 focus:outline-none focus:border-[#C7372C] transition-colors"
|
<input
|
||||||
value={settings.adminEmail}
|
type="email"
|
||||||
onChange={(e) => setSettings({ ...settings, adminEmail: e.target.value })}
|
className="col-span-2 border-b-2 py-2 focus:outline-none focus:border-[#C7372C] transition-colors"
|
||||||
/>
|
value={settings.adminEmail}
|
||||||
</div>
|
onChange={(e) => setSettings({ ...settings, adminEmail: e.target.value })}
|
||||||
|
/>
|
||||||
<div className="flex items-center justify-between py-4 border-t">
|
</div>
|
||||||
<div>
|
|
||||||
<p className="font-bold text-gray-800">WhatsApp Notifications</p>
|
<div className="flex items-center justify-between py-4 border-t">
|
||||||
<p className="text-sm text-gray-500">Send automatic alerts for new analysis signups</p>
|
<div>
|
||||||
</div>
|
<p className="font-bold text-gray-800">WhatsApp Notifications</p>
|
||||||
<button
|
<p className="text-sm text-gray-500">Send automatic alerts for new analysis signups</p>
|
||||||
onClick={() => setSettings({ ...settings, whatsappNotifications: !settings.whatsappNotifications })}
|
</div>
|
||||||
className={`w-12 h-6 rounded-full transition-colors relative ${settings.whatsappNotifications ? 'bg-green-500' : 'bg-gray-300'}`}
|
<button
|
||||||
>
|
onClick={() => setSettings({ ...settings, whatsappNotifications: !settings.whatsappNotifications })}
|
||||||
<div className={`absolute top-1 bg-white w-4 h-4 rounded-full transition-transform ${settings.whatsappNotifications ? 'translate-x-7' : 'translate-x-1'}`}></div>
|
className={`w-12 h-6 rounded-full transition-colors relative ${settings.whatsappNotifications ? 'bg-green-500' : 'bg-gray-300'}`}
|
||||||
</button>
|
>
|
||||||
</div>
|
<div className={`absolute top-1 bg-white w-4 h-4 rounded-full transition-transform ${settings.whatsappNotifications ? 'translate-x-7' : 'translate-x-1'}`}></div>
|
||||||
|
</button>
|
||||||
<div className="flex items-center justify-between py-4 border-t">
|
</div>
|
||||||
<div>
|
|
||||||
<p className="font-bold text-gray-800">Auto-Archive Reports</p>
|
<div className="flex items-center justify-between py-4 border-t">
|
||||||
<p className="text-sm text-gray-500">Automatically archive reports older than 30 days</p>
|
<div>
|
||||||
</div>
|
<p className="font-bold text-gray-800">Auto-Archive Reports</p>
|
||||||
<button
|
<p className="text-sm text-gray-500">Automatically archive reports older than 30 days</p>
|
||||||
onClick={() => setSettings({ ...settings, autoArchive: !settings.autoArchive })}
|
</div>
|
||||||
className={`w-12 h-6 rounded-full transition-colors relative ${settings.autoArchive ? 'bg-limelite-red' : 'bg-gray-300'}`}
|
<button
|
||||||
>
|
onClick={() => setSettings({ ...settings, autoArchive: !settings.autoArchive })}
|
||||||
<div className={`absolute top-1 bg-white w-4 h-4 rounded-full transition-transform ${settings.autoArchive ? 'translate-x-7' : 'translate-x-1'}`}></div>
|
className={`w-12 h-6 rounded-full transition-colors relative ${settings.autoArchive ? 'bg-limelite-red' : 'bg-gray-300'}`}
|
||||||
</button>
|
>
|
||||||
</div>
|
<div className={`absolute top-1 bg-white w-4 h-4 rounded-full transition-transform ${settings.autoArchive ? 'translate-x-7' : 'translate-x-1'}`}></div>
|
||||||
|
</button>
|
||||||
<div className="pt-6">
|
</div>
|
||||||
<button className="bg-limelite-red text-white px-8 py-3 font-bold uppercase tracking-widest hover:bg-limelite-maroon transition-colors">
|
|
||||||
Save Changes
|
<div className="pt-6">
|
||||||
</button>
|
<button className="bg-limelite-red text-white px-8 py-3 font-bold uppercase tracking-widest hover:bg-limelite-maroon transition-colors">
|
||||||
</div>
|
Save Changes
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
)}
|
||||||
|
</div>
|
||||||
{/* Support Modal */}
|
</main>
|
||||||
{showSupportModal && (
|
|
||||||
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
|
{/* Support Modal */}
|
||||||
<div className="bg-white w-full max-w-lg p-10 relative shadow-2xl animate-[fadeIn_0.2s_ease-out] rounded-none">
|
{showSupportModal && (
|
||||||
<div className="flex justify-between items-center mb-10">
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
|
||||||
<h2 className="text-4xl font-bold text-[#18181b]">Help</h2>
|
<div className="bg-white w-full max-w-lg p-10 relative shadow-2xl animate-[fadeIn_0.2s_ease-out] rounded-none">
|
||||||
<button
|
<div className="flex justify-between items-center mb-10">
|
||||||
onClick={() => setShowSupportModal(false)}
|
<h2 className="text-4xl font-bold text-[#18181b]">Help</h2>
|
||||||
className="text-gray-900 hover:text-black transition-colors"
|
<button
|
||||||
>
|
onClick={() => setShowSupportModal(false)}
|
||||||
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
className="text-gray-900 hover:text-black transition-colors"
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M6 18L18 6M6 6l12 12" />
|
>
|
||||||
</svg>
|
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
</button>
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M6 18L18 6M6 6l12 12" />
|
||||||
</div>
|
</svg>
|
||||||
|
</button>
|
||||||
<div className="space-y-8">
|
</div>
|
||||||
<div className="flex items-start gap-4">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-gray-500 mt-2.5 shrink-0"></div>
|
<div className="space-y-8">
|
||||||
<p className="text-xl text-gray-600 leading-relaxed">
|
<div className="flex items-start gap-4">
|
||||||
To access previous reports, please contact us at <i className="text-gray-800 font-medium">hello@limelite.com</i>
|
<div className="w-2 h-2 rounded-full bg-gray-500 mt-2.5 shrink-0"></div>
|
||||||
</p>
|
<p className="text-xl text-gray-600 leading-relaxed">
|
||||||
</div>
|
To access previous reports, please contact us at <i className="text-gray-800 font-medium">hello@limelite.com</i>
|
||||||
|
</p>
|
||||||
<div className="flex items-start gap-4">
|
</div>
|
||||||
<div className="w-2 h-2 rounded-full bg-gray-500 mt-2.5 shrink-0"></div>
|
|
||||||
<p className="text-xl text-gray-600 leading-relaxed">
|
<div className="flex items-start gap-4">
|
||||||
If you experience any technical issues, please contact us at <i className="text-gray-800 font-medium">support@example.com</i>
|
<div className="w-2 h-2 rounded-full bg-gray-500 mt-2.5 shrink-0"></div>
|
||||||
</p>
|
<p className="text-xl text-gray-600 leading-relaxed">
|
||||||
</div>
|
If you experience any technical issues, please contact us at <i className="text-gray-800 font-medium">support@example.com</i>
|
||||||
</div>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)}
|
||||||
};
|
</div>
|
||||||
|
);
|
||||||
export default DashboardPage;
|
};
|
||||||
|
|
||||||
|
export default DashboardPage;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-backend",
|
||||||
|
"build_pack": "dockerfile",
|
||||||
|
"base_directory": "/backend",
|
||||||
|
"dockerfile_location": "/Dockerfile",
|
||||||
|
"ports_exposes": "5000",
|
||||||
|
"git_repository": "https://mohanki:48d38a221949649fdf1b470bbd037de9a5e6c564@git.digifox.live/mohanki/limelite_faceanalyzer.git",
|
||||||
|
"git_branch": "master",
|
||||||
|
"custom_internal_name": "backend"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"docker_compose_raw": "version: '3.8'\n\nservices:\n frontend:\n build:\n context: ./frontend\n ports:\n - \"9500:80\"\n depends_on:\n - backend\n\n backend:\n build:\n context: ./backend\n ports:\n - \"9501:5000\"\n environment:\n - PORT=5000\n - MONGO_URI=mongodb://mongo:27017/limelite\n depends_on:\n - mongo\n\n mongo:\n image: mongo:latest\n ports:\n - \"9502:27017\"\n volumes:\n - mongo-data:/data/db\n\nvolumes:\n mongo-data: {}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-frontend",
|
||||||
|
"build_pack": "dockerfile",
|
||||||
|
"base_directory": "/frontend",
|
||||||
|
"dockerfile_location": "/Dockerfile",
|
||||||
|
"ports_exposes": "80",
|
||||||
|
"git_repository": "https://mohanki:48d38a221949649fdf1b470bbd037de9a5e6c564@git.digifox.live/mohanki/limelite_faceanalyzer.git",
|
||||||
|
"git_branch": "master",
|
||||||
|
"custom_internal_name": "frontend"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"fqdn": "http://limelite-faceanalyzer.houseofwebsites.ai"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"git_repository": "https://mohanki:48d38a221949649fdf1b470bbd037de9a5e6c564@git.digifox.live/mohanki/limelite_faceanalyzer.git"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"docker_compose_location": "docker-compose.yml"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-backend",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"server_uuid": "nt9z6kv7y3i644bzzsifjwnx",
|
||||||
|
"environment_name": "production",
|
||||||
|
"git_repository": "https://mohanki:48d38a221949649fdf1b470bbd037de9a5e6c564@git.digifox.live/mohanki/limelite_faceanalyzer.git",
|
||||||
|
"git_branch": "master",
|
||||||
|
"build_pack": "dockerfile",
|
||||||
|
"base_directory": "/backend",
|
||||||
|
"dockerfile_location": "/Dockerfile",
|
||||||
|
"ports_exposes": "5000",
|
||||||
|
"custom_internal_name": "backend"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-mongo",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"environment_name": "production",
|
||||||
|
"server_uuid": "jrcjtobvncyba40v3vi6pzqe"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-mongo",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"environment_name": "production",
|
||||||
|
"server_uuid": "nt9z6kv7y3i644bzzsifjwnx"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"key": "MONGO_URI",
|
||||||
|
"value": "mongodb://root:8Oef5zsT9dsAEsVnfREj8763ZTtuTFcUglMEvg1QvQt2wlki0Li6Ynmxy7kcU6wZ@77ll26aup0vxgzxxhko2dxot:27017/limelite?authSource=admin&directConnection=true"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"key": "MONGO_URI",
|
||||||
|
"value": "mongodb://root:TYXnbgf5b9uwaNRpeddIYx2B6ESSp7WLxCXaiSfLKayGhqwkgBDcR0p5HPRc9vGc@7jtniyem6lv8dlsex0c0ycv9:27017/limelite?authSource=admin&directConnection=true"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"key": "PORT",
|
||||||
|
"value": "5000"
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-frontend",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"server_uuid": "nt9z6kv7y3i644bzzsifjwnx",
|
||||||
|
"environment_name": "production",
|
||||||
|
"git_repository": "https://mohanki:48d38a221949649fdf1b470bbd037de9a5e6c564@git.digifox.live/mohanki/limelite_faceanalyzer.git",
|
||||||
|
"git_branch": "master",
|
||||||
|
"build_pack": "dockerfile",
|
||||||
|
"base_directory": "/frontend",
|
||||||
|
"dockerfile_location": "/Dockerfile",
|
||||||
|
"ports_exposes": "80",
|
||||||
|
"domains": "https://limelite-faceanalyzer.houseofwebsites.ai,https://www.limelite-faceanalyzer.houseofwebsites.ai",
|
||||||
|
"custom_internal_name": "frontend"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-mongo-test",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"server_uuid": "jrcjtobvncyba40v3vi6pzqe",
|
||||||
|
"environment_name": "production",
|
||||||
|
"docker_registry_image_name": "mongo",
|
||||||
|
"docker_registry_image_tag": "latest",
|
||||||
|
"build_pack": "dockerimage",
|
||||||
|
"ports_exposes": "27017"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-faceanalyzer-v3",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"server_uuid": "jrcjtobvncyba40v3vi6pzqe",
|
||||||
|
"environment_name": "production",
|
||||||
|
"git_repository": "https://git.digifox.live/mohanki/limelite_faceanalyzer.git",
|
||||||
|
"git_branch": "master",
|
||||||
|
"build_pack": "dockercompose",
|
||||||
|
"docker_compose_location": "/docker-compose.yml",
|
||||||
|
"ports_exposes": "9500"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "limelite-faceanalyzer-v4",
|
||||||
|
"project_uuid": "kozt3dmpa63penhin84sb8r0",
|
||||||
|
"server_uuid": "jrcjtobvncyba40v3vi6pzqe",
|
||||||
|
"environment_name": "production",
|
||||||
|
"git_repository": "https://git.digifox.live/mohanki/limelite_faceanalyzer.git",
|
||||||
|
"git_branch": "master",
|
||||||
|
"build_pack": "dockercompose",
|
||||||
|
"docker_compose_location": "/docker-compose.yml",
|
||||||
|
"docker_compose_raw": "version: '3.8'\n\nservices:\n frontend:\n build:\n context: ./frontend\n ports:\n - \"9500:80\"\n depends_on:\n - backend\n\n backend:\n build:\n context: ./backend\n ports:\n - \"9501:5000\"\n environment:\n - PORT=5000\n - MONGO_URI=mongodb://mongo:27017/limelite\n depends_on:\n - mongo\n\n mongo:\n image: mongo:latest\n ports:\n - \"9502:27017\"\n volumes:\n - mongo-data:/data/db\n\nvolumes:\n mongo-data: {}",
|
||||||
|
"ports_exposes": "9500"
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
const res = await fetch('https://deploy.digifox.live/api/v1/projects/kozt3dmpa63penhin84sb8r0/ijga3o6m6arup69yohj2ecyw', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer 4|lL3pgQVbfLb6KtUByDLaswjP4Jccny8HMj5jrs0v4ace4e78',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
for (const app of data.applications || []) {
|
||||||
|
console.log(`App: ${app.name}, UUID: ${app.uuid}`);
|
||||||
|
console.log(` Internal Name: ${app.settings?.custom_internal_name || 'NONE'}`);
|
||||||
|
console.log(` FQDN: ${app.fqdn}`);
|
||||||
|
console.log(` Repo: ${app.git_repository}#${app.git_branch}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run();
|
||||||
Reference in New Issue
Block a user