gentelella
Version:
Gentelella v4 — free admin template. 60 pages, 20 chart variants, fully interactive inbox & kanban, live theme generator, component playground, PWA-ready. Vite 8, vanilla JS, no Bootstrap, no jQuery.
180 lines (156 loc) • 6.89 kB
JavaScript
// Detail modals for project cards and contact cards. Imported by inline
// scripts in production/projects.html and production/contacts.html so each
// page stays self-contained while the modal markup lives here.
import { showModal } from './modal.js';
import { showToast } from './toast.js';
const MEMBER_NAMES = {
SK: { name: 'Sarah Kowalski', role: 'Designer', color: 'azure' },
MR: { name: 'Michael Reyes', role: 'Engineer', color: 'purple' },
EW: { name: 'Emily Wang', role: 'PM', color: 'yellow' },
MK: { name: 'Mark Kim', role: 'Engineer', color: 'red' },
LP: { name: 'Lina Park', role: 'Marketing', color: 'green' },
DR: { name: 'Diego Reyes', role: 'Sales', color: 'blue' },
YT: { name: 'Yuki Tanaka', role: 'Support', color: 'primary' },
TH: { name: 'Tom Hardy', role: 'Engineer', color: 'purple' },
A: { name: 'Aigars Silkalns', role: 'Admin', color: 'primary' }
};
const COLOR_VAR = {
primary: 'var(--primary)',
azure: 'var(--azure)',
purple: 'var(--purple)',
yellow: 'var(--yellow)',
red: 'var(--red)',
green: 'var(--green)',
blue: 'var(--blue)'
};
const STATUS_CLS = { green: 'status-green', yellow: 'status-yellow', red: 'status-red' };
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, (c) => (
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]
));
}
function emailFor(name) {
return name.toLowerCase().replace(/\s+/g, '.') + '@example.com';
}
const SAMPLE_TASKS = [
{ title: 'Wireframes review', done: true },
{ title: 'Stakeholder kickoff', done: true },
{ title: 'Design system tokens', done: true },
{ title: 'Component library', done: false },
{ title: 'QA + accessibility audit', done: false }
];
const SAMPLE_ACTIVITY = [
{ who: 'Sarah K.', what: 'commented on the design review', time: '12 min ago' },
{ who: 'Michael R.', what: 'merged PR #248', time: '1 hour ago' },
{ who: 'Emily W.', what: 'updated the timeline', time: '4 hours ago' }
];
export function openProjectModal({ title, client, status, sCls, desc, pct, due, members }) {
const memberRows = members.map((m) => {
const info = MEMBER_NAMES[m] || { name: m, role: 'Team member', color: 'primary' };
return `
<div class="detail-member">
<span class="av" style="background:${COLOR_VAR[info.color] || 'var(--primary)'}">${m}</span>
<span class="meta">
<span class="name">${escapeHtml(info.name)}</span>
<span class="role">${escapeHtml(info.role)}</span>
</span>
</div>
`;
}).join('');
const tasksHtml = SAMPLE_TASKS.map((t) => `
<div class="detail-task${t.done ? ' done' : ''}">
<span class="checkbox" aria-hidden="true">${t.done ? '<svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M2.5 6l2.5 3 5-6"/></svg>' : ''}</span>
<span class="task-title">${escapeHtml(t.title)}</span>
</div>
`).join('');
const activityHtml = SAMPLE_ACTIVITY.map((a) => `
<div class="detail-activity-item">
<div class="dot"></div>
<div><strong>${escapeHtml(a.who)}</strong> ${escapeHtml(a.what)} <span class="time">· ${escapeHtml(a.time)}</span></div>
</div>
`).join('');
const progressColor = sCls === 'red' ? 'var(--red)' : sCls === 'yellow' ? 'var(--yellow)' : 'var(--primary)';
const body = `
<div class="detail-project">
<div class="detail-header">
<div class="head-left">
<div class="client-tag">${escapeHtml(client)}</div>
<p class="desc">${escapeHtml(desc)}</p>
</div>
<span class="status ${STATUS_CLS[sCls] || 'status-green'}">${escapeHtml(status)}</span>
</div>
<div class="detail-stats">
<div class="stat-block">
<div class="label">Progress</div>
<div class="value">${pct}%</div>
<div class="progress-thin"><div class="bar" style="width:${pct}%;background:${progressColor}"></div></div>
</div>
<div class="stat-block">
<div class="label">Due date</div>
<div class="value">${escapeHtml(due)}</div>
</div>
<div class="stat-block">
<div class="label">Team</div>
<div class="value">${members.length}</div>
</div>
</div>
<div class="detail-section">
<div class="detail-section-title">Members</div>
<div class="detail-members">${memberRows}</div>
</div>
<div class="detail-section">
<div class="detail-section-title">Tasks · ${SAMPLE_TASKS.filter((t) => t.done).length}/${SAMPLE_TASKS.length}</div>
<div class="detail-tasks">${tasksHtml}</div>
</div>
<div class="detail-section">
<div class="detail-section-title">Recent activity</div>
<div class="detail-activity">${activityHtml}</div>
</div>
</div>
`;
showModal({
title,
body,
size: 'lg',
actions: [
{ label: 'Close', variant: 'outline' },
{ label: 'Open project', variant: 'primary', action: () => showToast(`Opening: ${title}`) }
]
});
}
export function openContactModal({ name, ini, color, role, projects, tasks, msgs }) {
const email = emailFor(name);
const phone = '+1 (555) ' + (300 + (ini.charCodeAt(0) % 600)).toString().padStart(3, '0') + '-' + (1000 + (ini.charCodeAt(ini.length - 1) % 9000)).toString();
const colorVar = COLOR_VAR[color] || 'var(--primary)';
const body = `
<div class="detail-contact">
<div class="detail-contact-head">
<div class="big-av" style="background:${colorVar}">${escapeHtml(ini)}</div>
<div>
<div class="name">${escapeHtml(name)}</div>
<div class="role">${escapeHtml(role)}</div>
</div>
</div>
<div class="detail-contact-fields">
<div><span class="label">Email</span><a class="value" href="mailto:${email}">${email}</a></div>
<div><span class="label">Phone</span><span class="value">${phone}</span></div>
<div><span class="label">Location</span><span class="value">San Francisco, CA</span></div>
<div><span class="label">Joined</span><span class="value">Mar 2024</span></div>
</div>
<div class="detail-contact-stats">
<div><strong>${projects}</strong><span>Projects</span></div>
<div><strong>${tasks}</strong><span>Open tasks</span></div>
<div><strong>${msgs}</strong><span>Unread messages</span></div>
</div>
</div>
`;
showModal({
title: name,
body,
actions: [
{ label: 'Close', variant: 'outline' },
{ label: 'Message', variant: 'outline', action: () => showToast(`Compose to: ${name}`) },
{ label: 'View profile', variant: 'primary', action: () => { window.location.href = 'profile.html'; } }
]
});
}