kei
This commit is contained in:
parent
47fd92e983
commit
1fafa67d4f
91
admin/applicant.php
Normal file
91
admin/applicant.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../includes/app.php';
|
||||
require_admin();
|
||||
ensure_applications_table();
|
||||
|
||||
$statusOptions = kei_status_options();
|
||||
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||
if ($id < 1) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newStatus = (string) ($_POST['status'] ?? 'review');
|
||||
if (isset($statusOptions[$newStatus])) {
|
||||
$stmt = db()->prepare('UPDATE talent_applications SET status = :status WHERE id = :id');
|
||||
$stmt->execute([':status' => $newStatus, ':id' => $id]);
|
||||
flash_set('admin_notice', 'Status kandidat diperbarui.');
|
||||
header('Location: applicant.php?id=' . $id);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = db()->prepare('SELECT * FROM talent_applications WHERE id = :id LIMIT 1');
|
||||
$stmt->execute([':id' => $id]);
|
||||
$applicant = $stmt->fetch();
|
||||
if (!$applicant) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
$notice = flash_get('admin_notice');
|
||||
render_head('Applicant Detail', 'Applicant detail in admin dashboard.', ['robots' => 'noindex, nofollow']);
|
||||
render_header('register');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="dashboard-head">
|
||||
<div>
|
||||
<span class="eyebrow">Applicant Detail</span>
|
||||
<h1 class="page-title mb-2"><?= h($applicant['full_name']) ?></h1>
|
||||
<p class="page-copy mb-0">Review data lengkap, media pendukung, dan update status kandidat.</p>
|
||||
</div>
|
||||
<a href="dashboard.php" class="btn btn-outline-dark">Kembali</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<?php if ($notice): ?>
|
||||
<div class="alert alert-success border-0 soft-alert mb-4"><?= h($notice) ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="row g-4 align-items-start">
|
||||
<div class="col-lg-7">
|
||||
<div class="app-card info-stack">
|
||||
<div class="detail-grid">
|
||||
<div><span>ID</span><strong>#<?= h((string) $applicant['id']) ?></strong></div>
|
||||
<div><span>Email</span><strong><?= h($applicant['email']) ?></strong></div>
|
||||
<div><span>WhatsApp</span><strong><?= h($applicant['whatsapp']) ?></strong></div>
|
||||
<div><span>Category</span><strong><?= h($applicant['category']) ?></strong></div>
|
||||
<div><span>Created</span><strong><?= h((string) $applicant['created_at']) ?></strong></div>
|
||||
<div><span>Status</span><strong><?= h(copy_text($statusOptions[$applicant['status']] ?? ['id' => $applicant['status'], 'jp' => $applicant['status']])) ?></strong></div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="media-links">
|
||||
<a class="btn btn-outline-dark btn-sm<?= empty($applicant['photo_url']) ? ' disabled' : '' ?>" href="../<?= h((string) $applicant['photo_url']) ?>"<?= empty($applicant['photo_url']) ? ' tabindex="-1" aria-disabled="true"' : '' ?>>Lihat Foto</a>
|
||||
<a class="btn btn-outline-dark btn-sm<?= empty($applicant['video_url']) ? ' disabled' : '' ?>" href="../<?= h((string) $applicant['video_url']) ?>"<?= empty($applicant['video_url']) ? ' tabindex="-1" aria-disabled="true"' : '' ?>>Lihat Video</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<form class="app-card form-stack" method="post">
|
||||
<span class="eyebrow">Update Status</span>
|
||||
<label class="form-label" for="status">Status review</label>
|
||||
<select class="form-select" id="status" name="status">
|
||||
<?php foreach ($statusOptions as $value => $label): ?>
|
||||
<option value="<?= h($value) ?>"<?= $applicant['status'] === $value ? ' selected' : '' ?>><?= h(copy_text($label)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-brand mt-4">Simpan Status</button>
|
||||
<div class="detail-panel mt-4">
|
||||
<p class="mb-0">Consent: <?= $applicant['consent_accuracy'] ? 'Data benar' : '-' ?> · <?= $applicant['consent_audition'] ? 'Audisi' : '-' ?> · <?= $applicant['consent_contract'] ? 'Kontrak' : '-' ?> · <?= $applicant['consent_data'] ? 'Izin data' : '-' ?></p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
133
admin/dashboard.php
Normal file
133
admin/dashboard.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../includes/app.php';
|
||||
require_admin();
|
||||
ensure_applications_table();
|
||||
|
||||
$statusOptions = kei_status_options();
|
||||
$q = trim((string) ($_GET['q'] ?? ''));
|
||||
$status = trim((string) ($_GET['status'] ?? ''));
|
||||
|
||||
if (isset($_GET['export']) && $_GET['export'] === 'csv') {
|
||||
$sql = 'SELECT id, full_name, email, whatsapp, category, status, created_at FROM talent_applications WHERE 1=1';
|
||||
$params = [];
|
||||
if ($q !== '') {
|
||||
$sql .= ' AND (full_name LIKE :q OR email LIKE :q OR whatsapp LIKE :q)';
|
||||
$params[':q'] = '%' . $q . '%';
|
||||
}
|
||||
if ($status !== '' && isset($statusOptions[$status])) {
|
||||
$sql .= ' AND status = :status';
|
||||
$params[':status'] = $status;
|
||||
}
|
||||
$sql .= ' ORDER BY created_at DESC';
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
header('Content-Type: text/csv; charset=utf-8');
|
||||
header('Content-Disposition: attachment; filename=kei-applicants-' . gmdate('Ymd-His') . '.csv');
|
||||
$out = fopen('php://output', 'w');
|
||||
fputcsv($out, ['ID', 'Name', 'Email', 'WhatsApp', 'Category', 'Status', 'Created At']);
|
||||
while ($row = $stmt->fetch()) {
|
||||
fputcsv($out, $row);
|
||||
}
|
||||
fclose($out);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = 'SELECT id, full_name, email, whatsapp, category, status, created_at FROM talent_applications WHERE 1=1';
|
||||
$params = [];
|
||||
if ($q !== '') {
|
||||
$sql .= ' AND (full_name LIKE :q OR email LIKE :q OR whatsapp LIKE :q)';
|
||||
$params[':q'] = '%' . $q . '%';
|
||||
}
|
||||
if ($status !== '' && isset($statusOptions[$status])) {
|
||||
$sql .= ' AND status = :status';
|
||||
$params[':status'] = $status;
|
||||
}
|
||||
$sql .= ' ORDER BY created_at DESC LIMIT 100';
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$applicants = $stmt->fetchAll();
|
||||
render_head('Admin Dashboard', 'Talent review dashboard for KOBA Entertainment.', ['robots' => 'noindex, nofollow']);
|
||||
render_header('register');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="dashboard-head">
|
||||
<div>
|
||||
<span class="eyebrow">Admin Dashboard</span>
|
||||
<h1 class="page-title mb-2">Talent applications</h1>
|
||||
<p class="page-copy mb-0">Cari, filter, dan buka detail kandidat untuk update status review.</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="dashboard.php?<?= h(http_build_query(['q' => $q, 'status' => $status, 'export' => 'csv'])) ?>" class="btn btn-outline-dark">Export CSV</a>
|
||||
<a href="logout.php" class="btn btn-ghost">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<form class="app-card filter-bar" method="get">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="q">Search</label>
|
||||
<input type="text" class="form-control" id="q" name="q" value="<?= h($q) ?>" placeholder="Nama, email, atau WhatsApp">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="status">Status</label>
|
||||
<select class="form-select" id="status" name="status">
|
||||
<option value="">Semua status</option>
|
||||
<?php foreach ($statusOptions as $value => $label): ?>
|
||||
<option value="<?= h($value) ?>"<?= $status === $value ? ' selected' : '' ?>><?= h(copy_text($label)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-grid">
|
||||
<button class="btn btn-brand" type="submit">Apply Filters</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="app-card table-card mt-4">
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Applicant</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!$applicants): ?>
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<div class="empty-state">Belum ada data yang cocok dengan filter saat ini.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($applicants as $applicant): ?>
|
||||
<tr>
|
||||
<td>#<?= h((string) $applicant['id']) ?></td>
|
||||
<td>
|
||||
<strong><?= h($applicant['full_name']) ?></strong>
|
||||
<div class="table-subtext"><?= h($applicant['email']) ?></div>
|
||||
</td>
|
||||
<td><?= h($applicant['category']) ?></td>
|
||||
<td><span class="<?= h(status_badge_class($applicant['status'])) ?>"><?= h(copy_text($statusOptions[$applicant['status']] ?? ['id' => $applicant['status'], 'jp' => $applicant['status']])) ?></span></td>
|
||||
<td><?= h((string) $applicant['created_at']) ?></td>
|
||||
<td class="text-end"><a href="applicant.php?id=<?= h((string) $applicant['id']) ?>" class="btn btn-sm btn-outline-dark">Detail</a></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
57
admin/login.php
Normal file
57
admin/login.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../includes/app.php';
|
||||
|
||||
$error = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
[$adminUser, $adminPass] = admin_credentials();
|
||||
$username = trim((string) ($_POST['username'] ?? ''));
|
||||
$password = (string) ($_POST['password'] ?? '');
|
||||
if (hash_equals($adminUser, $username) && hash_equals($adminPass, $password)) {
|
||||
$_SESSION['kei_admin_logged_in'] = true;
|
||||
$_SESSION['kei_admin_user'] = $username;
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
$error = 'Username atau password salah.';
|
||||
}
|
||||
|
||||
render_head('Admin Login', 'Admin login for KOBA Entertainment talent review dashboard.', ['robots' => 'noindex, nofollow']);
|
||||
render_header('register');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow">Admin</span>
|
||||
<h1 class="page-title">Talent Review Dashboard</h1>
|
||||
<p class="page-copy">Masuk untuk meninjau pendaftar, memfilter status, melihat detail, dan ekspor data CSV.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger border-0 soft-alert mb-4"><?= h($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<form class="app-card form-stack" method="post" data-submit-state>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-brand mt-4">Masuk Dashboard</button>
|
||||
<?php if (using_default_admin_credentials()): ?>
|
||||
<div class="detail-panel mt-4">
|
||||
<p class="mb-1"><strong>Demo credentials</strong></p>
|
||||
<p class="mb-0">admin@kei.local / KEIadmin2026!</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
6
admin/logout.php
Normal file
6
admin/logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../includes/app.php';
|
||||
unset($_SESSION['kei_admin_logged_in'], $_SESSION['kei_admin_user']);
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
32
article.php
Normal file
32
article.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$slug = isset($_GET['slug']) ? (string) $_GET['slug'] : '';
|
||||
$article = kei_news_by_slug($slug ?: 'creative-bridge-2026');
|
||||
if (!$article) {
|
||||
http_response_code(404);
|
||||
$article = kei_news()[0];
|
||||
}
|
||||
render_head(copy_text($article['title']), copy_text($article['excerpt']));
|
||||
render_header('news');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"><?= h($article['date']) ?></span>
|
||||
<h1 class="page-title"<?= copy_attrs($article['title']) ?>><?= h(copy_text($article['title'])) ?></h1>
|
||||
<p class="page-copy"<?= copy_attrs($article['excerpt']) ?>><?= h(copy_text($article['excerpt'])) ?></p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<article class="article-body">
|
||||
<?php foreach ($article['body'] as $paragraph): ?>
|
||||
<p<?= copy_attrs($paragraph) ?>><?= h(copy_text($paragraph)) ?></p>
|
||||
<?php endforeach; ?>
|
||||
</article>
|
||||
<a href="news.php" class="btn btn-outline-dark mt-3"<?= copy_attrs(['id' => 'Kembali ke Berita', 'jp' => 'ニュース一覧へ戻る']) ?>>Kembali ke Berita</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
@ -1,403 +1,366 @@
|
||||
:root {
|
||||
--kei-blue: #0e2a66;
|
||||
--kei-blue-strong: #081c45;
|
||||
--kei-ink: #121c2f;
|
||||
--kei-muted: #5f6b7c;
|
||||
--kei-bg: #f5f7fa;
|
||||
--kei-surface: #ffffff;
|
||||
--kei-surface-soft: #eef2f7;
|
||||
--kei-border: #d8e0ea;
|
||||
--kei-dark: #091120;
|
||||
--kei-success: #0f766e;
|
||||
--kei-danger: #b42318;
|
||||
--kei-radius-sm: 6px;
|
||||
--kei-radius-md: 10px;
|
||||
--kei-radius-lg: 14px;
|
||||
--kei-shadow: 0 18px 40px rgba(8, 28, 69, 0.08);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body {
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient 15s ease infinite;
|
||||
color: #212529;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: var(--kei-bg);
|
||||
color: var(--kei-ink);
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
display: flex;
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--kei-blue); }
|
||||
img { max-width: 100%; display: block; }
|
||||
|
||||
.kei-container { max-width: 1240px; padding-inline: clamp(20px, 5vw, 96px); margin-inline: auto; }
|
||||
.narrow-copy { max-width: 780px; }
|
||||
.section-space { padding-block: clamp(80px, 10vw, 120px); }
|
||||
.section-space-sm { padding-block: clamp(56px, 7vw, 88px); }
|
||||
.section-alt { background: var(--kei-surface-soft); }
|
||||
.border-top-soft { border-top: 1px solid var(--kei-border); }
|
||||
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1030;
|
||||
background: rgba(245, 247, 250, 0.94);
|
||||
backdrop-filter: blur(14px);
|
||||
border-bottom: 1px solid rgba(216, 224, 234, 0.9);
|
||||
}
|
||||
|
||||
.navbar { min-height: 78px; }
|
||||
.site-brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
color: var(--kei-ink);
|
||||
}
|
||||
.brand-mark {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: var(--kei-radius-sm);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 85vh;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.message {
|
||||
max-width: 85%;
|
||||
padding: 0.85rem 1.1rem;
|
||||
border-radius: 16px;
|
||||
line-height: 1.5;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
||||
animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
|
||||
.message.visitor {
|
||||
align-self: flex-end;
|
||||
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
|
||||
background: var(--kei-blue);
|
||||
color: #fff;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.message.bot {
|
||||
align-self: flex-start;
|
||||
background: #ffffff;
|
||||
color: #212529;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.chat-input-area {
|
||||
padding: 1.25rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.chat-input-area form {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.chat-input-area input {
|
||||
flex: 1;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
outline: none;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chat-input-area input:focus {
|
||||
border-color: #23a6d5;
|
||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
|
||||
}
|
||||
|
||||
.chat-input-area button {
|
||||
background: #212529;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chat-input-area button:hover {
|
||||
background: #000;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* Background Animations */
|
||||
.bg-animations {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blob {
|
||||
position: absolute;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
|
||||
}
|
||||
|
||||
.blob-1 {
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
background: rgba(238, 119, 82, 0.4);
|
||||
}
|
||||
|
||||
.blob-2 {
|
||||
bottom: -10%;
|
||||
right: -10%;
|
||||
background: rgba(35, 166, 213, 0.4);
|
||||
animation-delay: -7s;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.blob-3 {
|
||||
top: 40%;
|
||||
left: 30%;
|
||||
background: rgba(231, 60, 126, 0.3);
|
||||
animation-delay: -14s;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% { transform: translate(0, 0) rotate(0deg) scale(1); }
|
||||
33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
|
||||
66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
|
||||
100% { transform: translate(0, 0) rotate(360deg) scale(1); }
|
||||
}
|
||||
|
||||
.header-link {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Admin Styles */
|
||||
.admin-container {
|
||||
max-width: 900px;
|
||||
margin: 3rem auto;
|
||||
padding: 2.5rem;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.admin-container h1 {
|
||||
margin-top: 0;
|
||||
color: #212529;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.brand-copy { display: flex; flex-direction: column; line-height: 1.1; }
|
||||
.brand-copy strong { font-size: 0.96rem; font-weight: 700; }
|
||||
.brand-copy small { color: var(--kei-muted); font-size: 0.74rem; }
|
||||
|
||||
.nav-link {
|
||||
color: var(--kei-muted);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
padding: 10px 12px !important;
|
||||
border-radius: var(--kei-radius-sm);
|
||||
}
|
||||
.nav-link.active,
|
||||
.nav-link:hover,
|
||||
.nav-link:focus-visible {
|
||||
color: var(--kei-blue);
|
||||
background: rgba(14, 42, 102, 0.06);
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 8px;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 1rem;
|
||||
color: #6c757d;
|
||||
.btn {
|
||||
border-radius: var(--kei-radius-sm);
|
||||
padding: 0.82rem 1.2rem;
|
||||
font-weight: 600;
|
||||
border-width: 1px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
.btn:hover { transform: translateY(-1px); }
|
||||
.btn-brand {
|
||||
background: var(--kei-blue);
|
||||
color: #fff;
|
||||
border-color: var(--kei-blue);
|
||||
box-shadow: 0 12px 24px rgba(14, 42, 102, 0.16);
|
||||
}
|
||||
.btn-brand:hover,
|
||||
.btn-brand:focus-visible { background: var(--kei-blue-strong); color: #fff; border-color: var(--kei-blue-strong); }
|
||||
.btn-ghost {
|
||||
background: rgba(14, 42, 102, 0.04);
|
||||
color: var(--kei-ink);
|
||||
border: 1px solid var(--kei-border);
|
||||
}
|
||||
.btn-outline-dark { border-color: #b7c2d2; color: var(--kei-ink); }
|
||||
.btn-outline-dark:hover { background: var(--kei-dark); border-color: var(--kei-dark); color: #fff; }
|
||||
|
||||
.eyebrow {
|
||||
display: inline-block;
|
||||
font-size: 0.76rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.table td {
|
||||
background: #fff;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table tr td:first-child { border-radius: 12px 0 0 12px; }
|
||||
.table tr td:last-child { border-radius: 0 12px 12px 0; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
transition: all 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #23a6d5;
|
||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.admin-card {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
padding: 2rem;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
margin-bottom: 2.5rem;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.admin-card h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--kei-blue);
|
||||
font-weight: 700;
|
||||
}
|
||||
.display-title,
|
||||
.page-title,
|
||||
.section-title,
|
||||
.hero-stage-main h2,
|
||||
.footer-title {
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.03em;
|
||||
color: var(--kei-ink);
|
||||
}
|
||||
.display-title { font-size: clamp(2.7rem, 6vw, 4.9rem); line-height: 0.95; max-width: 11ch; }
|
||||
.page-title { font-size: clamp(2.2rem, 4vw, 3.6rem); line-height: 1.02; max-width: 15ch; }
|
||||
.section-title,
|
||||
.footer-title { font-size: clamp(1.8rem, 3vw, 3rem); line-height: 1.1; max-width: 18ch; }
|
||||
.lead-copy { font-size: clamp(1.1rem, 2vw, 1.45rem); color: var(--kei-blue-strong); font-weight: 600; margin-top: 1rem; }
|
||||
.support-copy,
|
||||
.page-copy,
|
||||
.section-copy,
|
||||
.hero-stage-main p,
|
||||
.footer-copy { max-width: 720px; color: var(--kei-muted); font-size: 1rem; }
|
||||
.section-heading-row { display: flex; justify-content: space-between; align-items: end; gap: 24px; margin-bottom: 1rem; }
|
||||
.text-link { color: var(--kei-blue); font-weight: 600; }
|
||||
|
||||
.btn-delete {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
.hero { min-height: calc(100vh - 78px); }
|
||||
.hero-stats { display: flex; flex-wrap: wrap; gap: 14px; }
|
||||
.stat-chip {
|
||||
min-width: 148px;
|
||||
padding: 16px 18px;
|
||||
border-radius: var(--kei-radius-md);
|
||||
border: 1px solid var(--kei-border);
|
||||
background: var(--kei-surface);
|
||||
box-shadow: var(--kei-shadow);
|
||||
}
|
||||
.stat-chip strong { display: block; font-size: 1.35rem; color: var(--kei-blue); }
|
||||
.stat-chip span { color: var(--kei-muted); font-size: 0.92rem; }
|
||||
.hero-stage {
|
||||
border-radius: var(--kei-radius-lg);
|
||||
background: var(--kei-dark);
|
||||
color: #fff;
|
||||
padding: clamp(24px, 4vw, 36px);
|
||||
box-shadow: 0 28px 60px rgba(9, 17, 32, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.hero-stage-main {
|
||||
padding: 22px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: var(--kei-radius-md);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.hero-stage-main h2, .hero-stage-main p, .stage-kicker { color: #fff; }
|
||||
.stage-kicker { font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.7; font-weight: 700; }
|
||||
.hero-stage-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
|
||||
.mini-panel {
|
||||
padding: 16px;
|
||||
border-radius: var(--kei-radius-md);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.mini-panel span { font-size: 0.74rem; opacity: 0.7; }
|
||||
.mini-panel strong { display: block; margin-top: 10px; font-size: 1rem; }
|
||||
.mini-panel p { color: rgba(255, 255, 255, 0.68); font-size: 0.88rem; margin: 10px 0 0; }
|
||||
|
||||
.service-card,
|
||||
.project-card,
|
||||
.news-card,
|
||||
.value-card,
|
||||
.quote-card,
|
||||
.philosophy-card,
|
||||
.app-card,
|
||||
.detail-panel,
|
||||
.contact-strip {
|
||||
background: var(--kei-surface);
|
||||
border: 1px solid var(--kei-border);
|
||||
border-radius: var(--kei-radius-lg);
|
||||
box-shadow: var(--kei-shadow);
|
||||
}
|
||||
.service-card,
|
||||
.project-card,
|
||||
.news-card,
|
||||
.value-card,
|
||||
.quote-card,
|
||||
.philosophy-card,
|
||||
.app-card { padding: 24px; }
|
||||
.service-card h3,
|
||||
.project-card h3,
|
||||
.news-card h3,
|
||||
.value-card h3,
|
||||
.quote-card h2,
|
||||
.philosophy-card h3,
|
||||
.app-card h2 { font-size: 1.28rem; font-weight: 700; margin-bottom: 0.85rem; }
|
||||
.service-code {
|
||||
display: inline-flex;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--kei-radius-sm);
|
||||
background: var(--kei-surface-soft);
|
||||
color: var(--kei-blue);
|
||||
font-weight: 800;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.service-code.large { width: 58px; height: 58px; font-size: 1.1rem; }
|
||||
.service-card p,
|
||||
.project-card p,
|
||||
.news-card p,
|
||||
.value-card p,
|
||||
.quote-card p,
|
||||
.philosophy-card p,
|
||||
.app-card p,
|
||||
.app-card li { color: var(--kei-muted); }
|
||||
.service-card small { display: block; margin-top: 1rem; color: var(--kei-blue); font-weight: 600; }
|
||||
.service-card:hover,
|
||||
.project-card:hover,
|
||||
.news-card:hover,
|
||||
.value-card:hover { transform: translateY(-4px); border-color: #c2cede; }
|
||||
.project-card { min-height: 280px; position: relative; overflow: hidden; }
|
||||
.project-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: auto 24px 24px auto;
|
||||
width: 48px;
|
||||
height: 2px;
|
||||
background: var(--kei-blue);
|
||||
}
|
||||
.project-card.tall { min-height: 320px; }
|
||||
.project-category,
|
||||
.news-date,
|
||||
.footer-label {
|
||||
display: inline-block;
|
||||
color: var(--kei-blue);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.cta-band { background: var(--kei-dark); color: #fff; }
|
||||
.cta-band .section-title, .cta-band .section-copy, .cta-band .eyebrow { color: #fff; }
|
||||
.cta-band .section-copy { opacity: 0.76; }
|
||||
.contact-strip { padding: clamp(24px, 4vw, 34px); display: flex; justify-content: space-between; align-items: center; gap: 24px; }
|
||||
|
||||
.quote-card,
|
||||
.philosophy-card { height: 100%; }
|
||||
.bullet-list,
|
||||
.ordered-list { padding-left: 1.1rem; margin-bottom: 0; }
|
||||
.mini-values { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 1.5rem; }
|
||||
.mini-values span {
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--kei-radius-sm);
|
||||
background: var(--kei-surface-soft);
|
||||
border: 1px solid var(--kei-border);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.service-detail {
|
||||
display: grid;
|
||||
grid-template-columns: 90px minmax(0, 1.3fr) minmax(0, 1fr);
|
||||
gap: 24px;
|
||||
padding-block: 26px;
|
||||
}
|
||||
.service-detail.with-border { border-bottom: 1px solid var(--kei-border); }
|
||||
.detail-panel { padding: 18px; }
|
||||
.app-card.form-stack,
|
||||
.app-card.info-stack { padding: clamp(22px, 4vw, 30px); }
|
||||
.form-label { font-weight: 600; font-size: 0.92rem; color: var(--kei-ink); }
|
||||
.form-control,
|
||||
.form-select,
|
||||
textarea {
|
||||
border-radius: var(--kei-radius-sm);
|
||||
border: 1px solid #c7d1df;
|
||||
padding: 0.84rem 0.95rem;
|
||||
color: var(--kei-ink);
|
||||
background: #fff;
|
||||
}
|
||||
.form-control:focus,
|
||||
.form-select:focus,
|
||||
textarea:focus {
|
||||
border-color: #8ca4cf;
|
||||
box-shadow: 0 0 0 0.18rem rgba(14, 42, 102, 0.08);
|
||||
}
|
||||
.form-hint { display: block; margin-top: 8px; color: var(--kei-muted); font-size: 0.82rem; }
|
||||
.consent-list { display: grid; gap: 12px; }
|
||||
.consent-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--kei-border);
|
||||
border-radius: var(--kei-radius-sm);
|
||||
background: var(--kei-surface-soft);
|
||||
}
|
||||
.soft-alert { border-radius: var(--kei-radius-md); box-shadow: var(--kei-shadow); }
|
||||
.ordered-list li + li,
|
||||
.bullet-list li + li { margin-top: 0.65rem; }
|
||||
|
||||
.dashboard-head { display: flex; justify-content: space-between; align-items: end; gap: 24px; }
|
||||
.filter-bar { padding: 22px; }
|
||||
.table-card { padding: 0; overflow: hidden; }
|
||||
.table thead th { background: var(--kei-surface-soft); color: var(--kei-muted); font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||
.table td, .table th { padding: 18px 20px; border-color: var(--kei-border); }
|
||||
.table-subtext { color: var(--kei-muted); font-size: 0.88rem; margin-top: 4px; }
|
||||
.empty-state { padding: 24px; color: var(--kei-muted); text-align: center; }
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
.detail-grid span { display: block; color: var(--kei-muted); font-size: 0.84rem; margin-bottom: 6px; }
|
||||
.detail-grid strong { font-size: 1rem; }
|
||||
.media-links { display: flex; gap: 12px; flex-wrap: wrap; }
|
||||
.article-body p + p { margin-top: 1rem; }
|
||||
|
||||
.site-footer { background: #fff; border-top: 1px solid var(--kei-border); }
|
||||
.footer-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; }
|
||||
.footer-grid div { display: flex; flex-direction: column; gap: 8px; }
|
||||
.footer-bottom {
|
||||
margin-top: 36px;
|
||||
padding-top: 18px;
|
||||
border-top: 1px solid var(--kei-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
color: var(--kei-muted);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
background: #212529;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 1rem;
|
||||
@media (max-width: 991.98px) {
|
||||
.hero { min-height: auto; }
|
||||
.display-title, .page-title, .section-title, .footer-title { max-width: 100%; }
|
||||
.section-heading-row, .dashboard-head, .contact-strip, .footer-bottom { flex-direction: column; align-items: flex-start; }
|
||||
.hero-stage-grid { grid-template-columns: 1fr; }
|
||||
.service-detail { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: #0088cc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
@media (max-width: 767.98px) {
|
||||
.navbar-collapse {
|
||||
background: rgba(255, 255, 255, 0.97);
|
||||
border: 1px solid var(--kei-border);
|
||||
border-radius: var(--kei-radius-md);
|
||||
padding: 18px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.detail-grid { grid-template-columns: 1fr; }
|
||||
.footer-grid { grid-template-columns: 1fr; }
|
||||
.hero-stats { display: grid; grid-template-columns: 1fr; }
|
||||
.contact-strip { align-items: flex-start; }
|
||||
}
|
||||
|
||||
.webhook-url {
|
||||
font-size: 0.85em;
|
||||
color: #555;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.history-table-container {
|
||||
overflow-x: auto;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.history-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.history-table-time {
|
||||
width: 15%;
|
||||
white-space: nowrap;
|
||||
font-size: 0.85em;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.history-table-user {
|
||||
width: 35%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.history-table-ai {
|
||||
width: 50%;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.no-messages {
|
||||
text-align: center;
|
||||
color: #777;
|
||||
}
|
||||
@ -1,39 +1,64 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const chatForm = document.getElementById('chat-form');
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
const langToggle = document.querySelector('[data-lang-toggle]');
|
||||
const langCurrent = document.querySelector('[data-lang-current]');
|
||||
const toastElement = document.getElementById('keiToast');
|
||||
const toastBody = toastElement ? toastElement.querySelector('.toast-body') : null;
|
||||
const toast = toastElement && window.bootstrap ? new bootstrap.Toast(toastElement, { delay: 1600 }) : null;
|
||||
|
||||
const appendMessage = (text, sender) => {
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.classList.add('message', sender);
|
||||
msgDiv.textContent = text;
|
||||
chatMessages.appendChild(msgDiv);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
const applyLanguage = (lang) => {
|
||||
document.documentElement.lang = lang === 'jp' ? 'ja' : 'id';
|
||||
document.querySelectorAll('[data-copy-id][data-copy-jp]').forEach((el) => {
|
||||
const content = lang === 'jp' ? el.dataset.copyJp : el.dataset.copyId;
|
||||
if (el.dataset.copyHtml === '1') {
|
||||
el.innerHTML = content || '';
|
||||
} else {
|
||||
el.textContent = content || '';
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('[data-placeholder-id][data-placeholder-jp]').forEach((el) => {
|
||||
el.setAttribute('placeholder', lang === 'jp' ? el.dataset.placeholderJp : el.dataset.placeholderId);
|
||||
});
|
||||
if (langCurrent) {
|
||||
langCurrent.textContent = lang.toUpperCase();
|
||||
}
|
||||
localStorage.setItem('kei_lang', lang);
|
||||
};
|
||||
|
||||
chatForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const message = chatInput.value.trim();
|
||||
if (!message) return;
|
||||
const showToast = (message) => {
|
||||
if (!toastBody || !toast) return;
|
||||
toastBody.textContent = message;
|
||||
toast.show();
|
||||
};
|
||||
|
||||
appendMessage(message, 'visitor');
|
||||
chatInput.value = '';
|
||||
const initialLang = localStorage.getItem('kei_lang') || 'id';
|
||||
applyLanguage(initialLang);
|
||||
|
||||
try {
|
||||
const response = await fetch('api/chat.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
// Artificial delay for realism
|
||||
setTimeout(() => {
|
||||
appendMessage(data.reply, 'bot');
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
|
||||
}
|
||||
if (langToggle) {
|
||||
langToggle.addEventListener('click', () => {
|
||||
const nextLang = (localStorage.getItem('kei_lang') || 'id') === 'id' ? 'jp' : 'id';
|
||||
applyLanguage(nextLang);
|
||||
showToast(nextLang === 'jp' ? '日本語に切り替えました。' : 'Bahasa Indonesia aktif.');
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-file-target]').forEach((input) => {
|
||||
input.addEventListener('change', (event) => {
|
||||
const target = document.getElementById(event.currentTarget.dataset.fileTarget);
|
||||
if (!target) return;
|
||||
const fileName = event.currentTarget.files && event.currentTarget.files[0] ? event.currentTarget.files[0].name : target.dataset.defaultLabel || target.textContent;
|
||||
if (!target.dataset.defaultLabel) {
|
||||
target.dataset.defaultLabel = target.textContent;
|
||||
}
|
||||
target.textContent = fileName;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-submit-state]').forEach((form) => {
|
||||
form.addEventListener('submit', () => {
|
||||
const submit = form.querySelector('button[type="submit"]');
|
||||
if (!submit) return;
|
||||
submit.disabled = true;
|
||||
submit.textContent = 'Processing...';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
98
contact.php
Normal file
98
contact.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
require_once __DIR__ . '/mail/MailService.php';
|
||||
|
||||
$errors = [];
|
||||
$success = false;
|
||||
$form = [
|
||||
'name' => trim((string) ($_POST['name'] ?? '')),
|
||||
'email' => trim((string) ($_POST['email'] ?? '')),
|
||||
'message' => trim((string) ($_POST['message'] ?? '')),
|
||||
];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($form['name'] === '' || mb_strlen($form['name']) < 2) {
|
||||
$errors[] = 'Nama minimal 2 karakter.';
|
||||
}
|
||||
if (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Email belum valid.';
|
||||
}
|
||||
if ($form['message'] === '' || mb_strlen($form['message']) < 10) {
|
||||
$errors[] = 'Pesan minimal 10 karakter.';
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$result = MailService::sendContactMessage($form['name'], $form['email'], $form['message'], null, 'KEI Website Contact');
|
||||
if (!empty($result['success'])) {
|
||||
$success = true;
|
||||
$form = ['name' => '', 'email' => '', 'message' => ''];
|
||||
} else {
|
||||
$errors[] = 'Pesan belum dapat dikirim saat ini. Silakan gunakan email perusahaan di bawah.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render_head('Contact', 'Official contact page for KOBA Entertainment Indonesia.');
|
||||
render_header('contact');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Contact', 'jp' => 'お問い合わせ']) ?>>Contact</span>
|
||||
<h1 class="page-title"<?= copy_attrs(['id' => 'Hubungi KOBA Entertainment Indonesia.', 'jp' => 'KOBA Entertainment Indonesiaへお問い合わせください。']) ?>>Hubungi KOBA Entertainment Indonesia.</h1>
|
||||
<p class="page-copy"<?= copy_attrs(['id' => 'Gunakan form ini untuk pertanyaan proyek, partnership, dan business inquiry. Untuk testing, form mengikuti konfigurasi MAIL_TO pada environment.', 'jp' => 'このフォームは、プロジェクト相談、提携、ビジネス問い合わせに利用できます。テスト時は環境変数MAIL_TOの設定に従います。']) ?>>Gunakan form ini untuk pertanyaan proyek, partnership, dan business inquiry. Untuk testing, form mengikuti konfigurasi MAIL_TO pada environment.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success border-0 soft-alert mb-4">Pesan Anda berhasil dikirim.</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($errors): ?>
|
||||
<div class="alert alert-danger border-0 soft-alert mb-4">
|
||||
<ul class="mb-0 ps-3">
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<li><?= h($error) ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="row g-4 align-items-start">
|
||||
<div class="col-lg-7">
|
||||
<form class="app-card form-stack" method="post" data-submit-state>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="name" class="form-label"<?= copy_attrs(['id' => 'Nama', 'jp' => 'お名前']) ?>>Nama</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?= h($form['name']) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="email" class="form-label"<?= copy_attrs(['id' => 'Email', 'jp' => 'メール']) ?>>Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="<?= h($form['email']) ?>" required>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="message" class="form-label"<?= copy_attrs(['id' => 'Pesan', 'jp' => 'メッセージ']) ?>>Pesan</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="6" required><?= h($form['message']) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-brand mt-4" data-submit-label="Kirim Pesan"<?= copy_attrs(['id' => 'Kirim Pesan', 'jp' => '送信']) ?>>Kirim Pesan</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<aside class="app-card info-stack">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Info', 'jp' => '情報']) ?>>Info</span>
|
||||
<h2<?= copy_attrs(['id' => 'Email perusahaan dan lokasi operasional.', 'jp' => '会社メールと活動拠点。']) ?>>Email perusahaan dan lokasi operasional.</h2>
|
||||
<p class="mb-1"><strong>Email</strong></p>
|
||||
<p>hello@kobaentertainment.id</p>
|
||||
<p class="mb-1 mt-4"><strong<?= copy_attrs(['id' => 'Lokasi', 'jp' => '所在地']) ?>>Lokasi</strong></p>
|
||||
<p>Jakarta, Indonesia</p>
|
||||
<div class="detail-panel mt-4">
|
||||
<p class="mb-0"<?= copy_attrs(['id' => 'This is for testing purposes only — Flatlogic does not guarantee usage of the mail server. Please set up your own SMTP in .env (MAIL_/SMTP_ vars) with our AI Agent.', 'jp' => 'これはテスト用です。Flatlogicはメールサーバーの利用を保証しません。.env の MAIL_/SMTP_ 設定を独自に構成してください。']) ?>>This is for testing purposes only — Flatlogic does not guarantee usage of the mail server. Please set up your own SMTP in .env (MAIL_/SMTP_ vars) with our AI Agent.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
21
db/migrations/001_create_talent_applications.sql
Normal file
21
db/migrations/001_create_talent_applications.sql
Normal file
@ -0,0 +1,21 @@
|
||||
-- KEI initial migration
|
||||
-- Creates the table used by register.php and the admin review dashboard.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS talent_applications (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
full_name VARCHAR(190) NOT NULL,
|
||||
email VARCHAR(190) NOT NULL,
|
||||
whatsapp VARCHAR(50) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL,
|
||||
photo_url VARCHAR(255) DEFAULT NULL,
|
||||
video_url VARCHAR(255) DEFAULT NULL,
|
||||
consent_accuracy TINYINT(1) NOT NULL DEFAULT 0,
|
||||
consent_audition TINYINT(1) NOT NULL DEFAULT 0,
|
||||
consent_contract TINYINT(1) NOT NULL DEFAULT 0,
|
||||
consent_data TINYINT(1) NOT NULL DEFAULT 0,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'review',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_talent_applications_status (status),
|
||||
INDEX idx_talent_applications_created_at (created_at),
|
||||
INDEX idx_talent_applications_email (email)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
12
healthz.php
Normal file
12
healthz.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
try {
|
||||
ensure_applications_table();
|
||||
db()->query('SELECT 1');
|
||||
echo json_encode(['status' => 'ok', 'time' => gmdate('c'), 'db' => 'ok'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $exception) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['status' => 'error', 'time' => gmdate('c'), 'db' => 'error'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
266
includes/app.php
Normal file
266
includes/app.php
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/content.php';
|
||||
|
||||
function h(?string $value): string
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
function copy_text(array $copy, string $lang = 'id'): string
|
||||
{
|
||||
return $copy[$lang] ?? ($copy['id'] ?? '');
|
||||
}
|
||||
|
||||
function copy_attrs(array $copy, bool $html = false): string
|
||||
{
|
||||
$extra = $html ? ' data-copy-html="1"' : '';
|
||||
return ' data-copy-id="' . h($copy['id'] ?? '') . '" data-copy-jp="' . h($copy['jp'] ?? '') . '"' . $extra;
|
||||
}
|
||||
|
||||
function placeholder_attrs(array $copy): string
|
||||
{
|
||||
return ' data-placeholder-id="' . h($copy['id'] ?? '') . '" data-placeholder-jp="' . h($copy['jp'] ?? '') . '"';
|
||||
}
|
||||
|
||||
function asset_url(string $path): string
|
||||
{
|
||||
$full = __DIR__ . '/../' . ltrim($path, '/');
|
||||
$version = is_file($full) ? (string) filemtime($full) : (string) time();
|
||||
return $path . '?v=' . $version;
|
||||
}
|
||||
|
||||
function project_name(): string
|
||||
{
|
||||
return $_SERVER['PROJECT_NAME'] ?? 'KOBA Entertainment Indonesia';
|
||||
}
|
||||
|
||||
function page_title(string $title): string
|
||||
{
|
||||
return $title . ' | ' . project_name();
|
||||
}
|
||||
|
||||
function render_head(string $title, string $description, array $options = []): void
|
||||
{
|
||||
$pageDescription = $description;
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? $pageDescription;
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
$robots = $options['robots'] ?? 'index, follow';
|
||||
echo '<!doctype html><html lang="id"><head>';
|
||||
echo '<meta charset="utf-8" />';
|
||||
echo '<meta name="viewport" content="width=device-width, initial-scale=1" />';
|
||||
echo '<title>' . h(page_title($title)) . '</title>';
|
||||
echo '<meta name="description" content="' . h($projectDescription ?: $pageDescription) . '" />';
|
||||
echo '<meta name="robots" content="' . h($robots) . '" />';
|
||||
echo '<meta name="theme-color" content="#0e2a66" />';
|
||||
echo '<meta property="og:title" content="' . h(page_title($title)) . '" />';
|
||||
echo '<meta property="og:description" content="' . h($projectDescription ?: $pageDescription) . '" />';
|
||||
echo '<meta property="og:type" content="website" />';
|
||||
echo '<meta property="twitter:card" content="summary_large_image" />';
|
||||
echo '<meta property="twitter:title" content="' . h(page_title($title)) . '" />';
|
||||
echo '<meta property="twitter:description" content="' . h($projectDescription ?: $pageDescription) . '" />';
|
||||
if ($projectImageUrl) {
|
||||
echo '<meta property="og:image" content="' . h($projectImageUrl) . '" />';
|
||||
echo '<meta property="twitter:image" content="' . h($projectImageUrl) . '" />';
|
||||
}
|
||||
echo '<link rel="preconnect" href="https://fonts.googleapis.com">';
|
||||
echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>';
|
||||
echo '<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">';
|
||||
echo '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">';
|
||||
echo '<link rel="stylesheet" href="' . h(asset_url('assets/css/custom.css')) . '">';
|
||||
echo '</head><body>';
|
||||
}
|
||||
|
||||
function render_header(string $active = 'home'): void
|
||||
{
|
||||
$navItems = kei_nav_items();
|
||||
?>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg navbar-light py-0">
|
||||
<div class="container-fluid kei-container">
|
||||
<a class="navbar-brand site-brand" href="index.php" aria-label="KOBA Entertainment Indonesia homepage">
|
||||
<span class="brand-mark">KEI</span>
|
||||
<span class="brand-copy">
|
||||
<strong>KOBA Entertainment Indonesia</strong>
|
||||
<small>Modern entertainment bridge</small>
|
||||
</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#keiNav" aria-controls="keiNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse justify-content-end" id="keiNav">
|
||||
<ul class="navbar-nav align-items-lg-center gap-lg-1 mb-3 mb-lg-0">
|
||||
<?php foreach ($navItems as $item): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $active === $item['key'] ? 'active' : '' ?>" href="<?= h($item['href']) ?>"<?= copy_attrs($item['label']) ?>><?= h(copy_text($item['label'])) ?></a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center gap-2 ms-lg-3">
|
||||
<button class="btn btn-ghost btn-sm" type="button" data-lang-toggle aria-label="Switch language">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1Zm4.95 6h-2.16a11.8 11.8 0 0 0-.74-3.16A5.53 5.53 0 0 1 12.95 7ZM8 2.52c.6.82 1.25 2.34 1.55 4.48H6.45C6.75 4.86 7.4 3.34 8 2.52ZM5.95 3.84A11.8 11.8 0 0 0 5.21 7H3.05a5.53 5.53 0 0 1 2.9-3.16ZM3.05 9h2.16c.1 1.12.36 2.2.74 3.16A5.53 5.53 0 0 1 3.05 9Zm4.95 4.48c-.6-.82-1.25-2.34-1.55-4.48h3.1c-.3 2.14-.95 3.66-1.55 4.48Zm2.05-1.32c.38-.96.64-2.04.74-3.16h2.16a5.53 5.53 0 0 1-2.9 3.16Z"/></svg>
|
||||
<span class="ms-2" data-lang-current>ID</span>
|
||||
</button>
|
||||
<a class="btn btn-brand btn-sm" href="register.php"<?= copy_attrs(['id' => 'Daftar Sekarang', 'jp' => '今すぐ応募']) ?>>Daftar Sekarang</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<?php
|
||||
}
|
||||
|
||||
function render_footer(): void
|
||||
{
|
||||
?>
|
||||
<footer class="site-footer section-space-sm">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="row g-4 align-items-end">
|
||||
<div class="col-lg-6">
|
||||
<p class="eyebrow mb-3"<?= copy_attrs(['id' => 'KOBA Entertainment Indonesia', 'jp' => 'KOBA Entertainment Indonesia']) ?>>KOBA Entertainment Indonesia</p>
|
||||
<h2 class="footer-title"<?= copy_attrs(['id' => 'Building global entertainment from Indonesia.', 'jp' => 'インドネシアから世界水準のエンターテインメントを築く。']) ?>>Building global entertainment from Indonesia.</h2>
|
||||
<p class="footer-copy"<?= copy_attrs(['id' => 'Situs resmi untuk branding perusahaan, kolaborasi proyek, dan konversi talent baru yang siap tumbuh bersama KEI.', 'jp' => '本サイトは、企業ブランディング、プロジェクト連携、新しいタレント募集のための公式窓口です。']) ?>>Situs resmi untuk branding perusahaan, kolaborasi proyek, dan konversi talent baru yang siap tumbuh bersama KEI.</p>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="footer-grid">
|
||||
<div>
|
||||
<span class="footer-label"<?= copy_attrs(['id' => 'Kontak', 'jp' => '連絡先']) ?>>Kontak</span>
|
||||
<a href="mailto:hello@kobaentertainment.id">hello@kobaentertainment.id</a>
|
||||
<span>Jakarta, Indonesia</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="footer-label"<?= copy_attrs(['id' => 'Navigasi', 'jp' => 'ナビゲーション']) ?>>Navigasi</span>
|
||||
<a href="profile.php">Profil</a>
|
||||
<a href="news.php">Berita</a>
|
||||
<a href="admin/login.php">Admin Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<span>© <?= date('Y') ?> KOBA Entertainment Indonesia</span>
|
||||
<a href="healthz.php">/healthz</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3">
|
||||
<div id="keiToast" class="toast align-items-center text-bg-dark border-0" role="status" aria-live="polite" aria-atomic="true">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">Bahasa Indonesia aktif.</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous" defer></script>
|
||||
<script src="<?= h(asset_url('assets/js/main.js')) ?>" defer></script>
|
||||
</body></html>
|
||||
<?php
|
||||
}
|
||||
|
||||
function flash_set(string $key, $value): void
|
||||
{
|
||||
$_SESSION['_flash'][$key] = $value;
|
||||
}
|
||||
|
||||
function flash_get(string $key)
|
||||
{
|
||||
$value = $_SESSION['_flash'][$key] ?? null;
|
||||
unset($_SESSION['_flash'][$key]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
function ensure_applications_table(): void
|
||||
{
|
||||
$sql = "CREATE TABLE IF NOT EXISTS talent_applications (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
full_name VARCHAR(190) NOT NULL,
|
||||
email VARCHAR(190) NOT NULL,
|
||||
whatsapp VARCHAR(50) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL,
|
||||
photo_url VARCHAR(255) DEFAULT NULL,
|
||||
video_url VARCHAR(255) DEFAULT NULL,
|
||||
consent_accuracy TINYINT(1) NOT NULL DEFAULT 0,
|
||||
consent_audition TINYINT(1) NOT NULL DEFAULT 0,
|
||||
consent_contract TINYINT(1) NOT NULL DEFAULT 0,
|
||||
consent_data TINYINT(1) NOT NULL DEFAULT 0,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'review',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
|
||||
db()->exec($sql);
|
||||
}
|
||||
|
||||
function normalize_whatsapp(string $value): string
|
||||
{
|
||||
$value = preg_replace('/[^0-9+]/', '', $value) ?? '';
|
||||
return trim($value);
|
||||
}
|
||||
|
||||
function handle_optional_upload(string $field, array $allowedMimeMap, int $maxBytes, string $subdir): ?string
|
||||
{
|
||||
if (empty($_FILES[$field]) || (int) $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$file = $_FILES[$field];
|
||||
if ((int) $file['error'] !== UPLOAD_ERR_OK) {
|
||||
throw new RuntimeException('Upload gagal untuk ' . $field . '.');
|
||||
}
|
||||
if ((int) $file['size'] > $maxBytes) {
|
||||
throw new RuntimeException('Ukuran file ' . $field . ' terlalu besar.');
|
||||
}
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
$mime = (string) $finfo->file($file['tmp_name']);
|
||||
if (!isset($allowedMimeMap[$mime])) {
|
||||
throw new RuntimeException('Format file ' . $field . ' tidak didukung.');
|
||||
}
|
||||
$targetDir = __DIR__ . '/../assets/uploads/' . $subdir;
|
||||
if (!is_dir($targetDir) && !mkdir($targetDir, 0775, true) && !is_dir($targetDir)) {
|
||||
throw new RuntimeException('Folder upload tidak dapat dibuat.');
|
||||
}
|
||||
$filename = $subdir . '-' . date('YmdHis') . '-' . bin2hex(random_bytes(4)) . '.' . $allowedMimeMap[$mime];
|
||||
$target = $targetDir . '/' . $filename;
|
||||
if (!move_uploaded_file($file['tmp_name'], $target)) {
|
||||
throw new RuntimeException('Gagal menyimpan file ' . $field . '.');
|
||||
}
|
||||
return 'assets/uploads/' . $subdir . '/' . $filename;
|
||||
}
|
||||
|
||||
function admin_credentials(): array
|
||||
{
|
||||
$user = getenv('KEI_ADMIN_USER') ?: (getenv('ADMIN_EMAIL') ?: 'admin@kei.local');
|
||||
$pass = getenv('KEI_ADMIN_PASSWORD') ?: (getenv('ADMIN_PASSWORD') ?: 'KEIadmin2026!');
|
||||
return [$user, $pass];
|
||||
}
|
||||
|
||||
function using_default_admin_credentials(): bool
|
||||
{
|
||||
return !getenv('KEI_ADMIN_USER') && !getenv('ADMIN_EMAIL') && !getenv('KEI_ADMIN_PASSWORD') && !getenv('ADMIN_PASSWORD');
|
||||
}
|
||||
|
||||
function is_admin(): bool
|
||||
{
|
||||
return !empty($_SESSION['kei_admin_logged_in']);
|
||||
}
|
||||
|
||||
function require_admin(): void
|
||||
{
|
||||
if (!is_admin()) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function status_badge_class(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'accepted' => 'badge bg-success-subtle text-success-emphasis',
|
||||
'rejected' => 'badge bg-danger-subtle text-danger-emphasis',
|
||||
default => 'badge bg-secondary-subtle text-secondary-emphasis',
|
||||
};
|
||||
}
|
||||
163
includes/content.php
Normal file
163
includes/content.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
function kei_nav_items(): array
|
||||
{
|
||||
return [
|
||||
['key' => 'home', 'href' => 'index.php', 'label' => ['id' => 'Home', 'jp' => 'ホーム']],
|
||||
['key' => 'profile', 'href' => 'profile.php', 'label' => ['id' => 'Profil', 'jp' => 'プロフィール']],
|
||||
['key' => 'services', 'href' => 'services.php', 'label' => ['id' => 'Layanan', 'jp' => 'サービス']],
|
||||
['key' => 'projects', 'href' => 'projects.php', 'label' => ['id' => 'Proyek', 'jp' => 'プロジェクト']],
|
||||
['key' => 'news', 'href' => 'news.php', 'label' => ['id' => 'Berita', 'jp' => 'ニュース']],
|
||||
['key' => 'register', 'href' => 'register.php', 'label' => ['id' => 'Daftar', 'jp' => '応募']],
|
||||
['key' => 'contact', 'href' => 'contact.php', 'label' => ['id' => 'Contact', 'jp' => 'お問い合わせ']],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_stats(): array
|
||||
{
|
||||
return [
|
||||
['number' => '50+', 'label' => ['id' => 'Projects', 'jp' => 'プロジェクト']],
|
||||
['number' => '20+', 'label' => ['id' => 'Artists', 'jp' => 'アーティスト']],
|
||||
['number' => '10+', 'label' => ['id' => 'Collaborations', 'jp' => 'コラボレーション']],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_services(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'code' => 'MP',
|
||||
'title' => ['id' => 'Music Production', 'jp' => '音楽制作'],
|
||||
'description' => ['id' => 'Produksi musik end-to-end dengan arahan artistik, recording, mixing, dan mastering yang presisi.', 'jp' => 'アーティスティックディレクション、レコーディング、ミックス、マスタリングまで一貫して対応します。'],
|
||||
'benefit' => ['id' => 'Ideal untuk single, soundtrack, dan proyek lintas negara.', 'jp' => 'シングル、サウンドトラック、国際共同制作に最適です。'],
|
||||
],
|
||||
[
|
||||
'code' => 'AM',
|
||||
'title' => ['id' => 'Artist Management', 'jp' => 'アーティストマネジメント'],
|
||||
'description' => ['id' => 'Pendampingan strategi karier, positioning brand, dan pengembangan talent yang berkelanjutan.', 'jp' => 'キャリア戦略、ブランドポジショニング、継続的なタレント育成を支援します。'],
|
||||
'benefit' => ['id' => 'Fokus pada pertumbuhan jangka panjang dan market fit.', 'jp' => '長期的な成長と市場適合性を重視します。'],
|
||||
],
|
||||
[
|
||||
'code' => 'EV',
|
||||
'title' => ['id' => 'Event & Live Show', 'jp' => 'イベント & ライブショー'],
|
||||
'description' => ['id' => 'Konsep panggung, produksi acara, dan pengalaman live yang tertata dengan disiplin internasional.', 'jp' => '国際基準の進行管理で、ステージ設計から本番運営まで実行します。'],
|
||||
'benefit' => ['id' => 'Cocok untuk showcase, corporate event, dan special performance.', 'jp' => 'ショーケース、企業イベント、特別公演に対応します。'],
|
||||
],
|
||||
[
|
||||
'code' => 'CC',
|
||||
'title' => ['id' => 'Creative Content', 'jp' => 'クリエイティブコンテンツ'],
|
||||
'description' => ['id' => 'Konten visual dan editorial untuk memperkuat narasi brand, artis, dan campaign.', 'jp' => 'ブランド、アーティスト、キャンペーンの物語を強化する映像・編集コンテンツを制作します。'],
|
||||
'benefit' => ['id' => 'Dari teaser, music video, sampai social rollout.', 'jp' => 'ティーザー、MV、ソーシャル展開まで一体で設計します。'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_projects(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'category' => ['id' => 'Event', 'jp' => 'イベント'],
|
||||
'title' => ['id' => 'Jakarta Live Session', 'jp' => 'ジャカルタ・ライブセッション'],
|
||||
'summary' => ['id' => 'Showcase intimate dengan tata produksi yang cinematic dan line-up kolaboratif lintas talenta.', 'jp' => 'シネマティックな演出で多彩なタレントが共演したインティメイトなショーケース。'],
|
||||
],
|
||||
[
|
||||
'category' => ['id' => 'Music Video', 'jp' => 'ミュージックビデオ'],
|
||||
'title' => ['id' => 'Blue Frame Visual Series', 'jp' => 'ブルーフレーム・ビジュアルシリーズ'],
|
||||
'summary' => ['id' => 'Seri visual musik yang menonjolkan estetika premium, ritme editing presisi, dan identitas global.', 'jp' => 'プレミアムな美学と精密な編集リズムを備えたグローバル志向の映像シリーズ。'],
|
||||
],
|
||||
[
|
||||
'category' => ['id' => 'Collaboration', 'jp' => 'コラボレーション'],
|
||||
'title' => ['id' => 'Tokyo–Jakarta Creator Lab', 'jp' => '東京–ジャカルタ クリエイターラボ'],
|
||||
'summary' => ['id' => 'Program kolaborasi kreator Jepang dan Indonesia untuk membangun format hiburan baru.', 'jp' => '日本とインドネシアのクリエイターが新しいエンターテインメント形式を共創するプログラム。'],
|
||||
],
|
||||
[
|
||||
'category' => ['id' => 'Talent Showcase', 'jp' => 'タレントショーケース'],
|
||||
'title' => ['id' => 'New Voices Stage', 'jp' => 'ニュー・ボイシズ・ステージ'],
|
||||
'summary' => ['id' => 'Platform audisi dan presentasi talenta baru dengan pendekatan kurasi yang terarah.', 'jp' => 'キュレーション重視で新しい才能を紹介するオーディション・プレゼンテーション。'],
|
||||
],
|
||||
[
|
||||
'category' => ['id' => 'Campaign', 'jp' => 'キャンペーン'],
|
||||
'title' => ['id' => 'Culture Moves Forward', 'jp' => 'カルチャー・ムーブス・フォワード'],
|
||||
'summary' => ['id' => 'Kampanye brand entertainment yang memadukan storytelling budaya dan eksekusi modern.', 'jp' => '文化的ストーリーテリングと現代的な実行力を融合したエンタメブランドキャンペーン。'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_values(): array
|
||||
{
|
||||
return [
|
||||
['title' => ['id' => 'Creative Bridge', 'jp' => 'Creative Bridge'], 'body' => ['id' => 'Menghubungkan ide, orang, dan pasar lintas budaya.', 'jp' => '文化を越えてアイデア、人、マーケットをつなぎます。']],
|
||||
['title' => ['id' => 'Quality', 'jp' => 'Quality'], 'body' => ['id' => 'Standar kerja disiplin, detail, dan konsisten.', 'jp' => '規律、細部、継続性を重視した品質基準。']],
|
||||
['title' => ['id' => 'Cultural Synergy', 'jp' => 'Cultural Synergy'], 'body' => ['id' => 'Perpaduan rasa lokal dengan visi internasional.', 'jp' => 'ローカルな感性と国際的な視点の融合。']],
|
||||
['title' => ['id' => 'Growth', 'jp' => 'Growth'], 'body' => ['id' => 'Mendorong artis dan mitra bertumbuh bersama.', 'jp' => 'アーティストとパートナーの共成長を促します。']],
|
||||
['title' => ['id' => 'Integrity', 'jp' => 'Integrity'], 'body' => ['id' => 'Kepercayaan dibangun melalui komitmen dan transparansi.', 'jp' => '信頼は誠実さと透明性から生まれます。']],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_news(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'slug' => 'creative-bridge-2026',
|
||||
'date' => '14 Apr 2026',
|
||||
'title' => ['id' => 'KEI Memperluas Creative Bridge 2026', 'jp' => 'KEIがCreative Bridge 2026を拡張'],
|
||||
'excerpt' => ['id' => 'Kolaborasi baru Indonesia–Jepang dibuka untuk artis, kreator, dan format pertunjukan lintas disiplin.', 'jp' => 'インドネシアと日本の新たな協業枠が、アーティスト・クリエイター・複合型パフォーマンス向けに公開されました。'],
|
||||
'body' => [
|
||||
['id' => 'KOBA Entertainment Indonesia membuka fase baru Creative Bridge 2026 untuk mempertemukan artis, produser, dan kreator visual dari Indonesia dan Jepang dalam satu pipeline kerja yang lebih terstruktur.', 'jp' => 'KOBA Entertainment Indonesiaは、インドネシアと日本のアーティスト、プロデューサー、映像クリエイターを、より体系化された制作パイプラインで結ぶCreative Bridge 2026の新フェーズを開始しました。'],
|
||||
['id' => 'Fokus utamanya adalah membangun proyek yang siap tampil, siap dipublikasikan, dan siap berkolaborasi dengan standar komunikasi serta produksi yang konsisten.', 'jp' => '主な焦点は、発表・公開・共同制作に適したプロジェクトを、安定したコミュニケーションと制作基準のもとで育てることです。'],
|
||||
['id' => 'Program ini juga menjadi kanal kurasi awal untuk talenta baru yang mendaftar melalui halaman Daftar di website resmi KEI.', 'jp' => 'このプログラムは、公式サイトの応募ページから登録した新しいタレントを選考する初期チャネルとしても機能します。'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'new-voices-stage',
|
||||
'date' => '08 Apr 2026',
|
||||
'title' => ['id' => 'New Voices Stage Siapkan Audisi Terarah', 'jp' => 'New Voices Stageが戦略的オーディションを準備'],
|
||||
'excerpt' => ['id' => 'Program showcase baru dirancang untuk menyeleksi performer dengan kesiapan panggung dan karakter artistik kuat.', 'jp' => '新しいショーケース・プログラムは、舞台対応力と明確なアーティスト性を持つ出演者を選抜するために設計されました。'],
|
||||
'body' => [
|
||||
['id' => 'Melalui New Voices Stage, KEI merancang jalur kurasi yang lebih fokus untuk penyanyi, talent, dan kreator yang siap diuji dalam format presentasi singkat namun terukur.', 'jp' => 'New Voices Stageを通じて、KEIは歌手・タレント・クリエイター向けに、短時間でも評価可能なプレゼン形式の選考導線を設計しました。'],
|
||||
['id' => 'Setiap kandidat akan dipetakan berdasarkan potensi performance, kesiapan materi, dan kecocokan terhadap kebutuhan proyek yang sedang aktif.', 'jp' => '各候補者は、パフォーマンスの可能性、素材の完成度、進行中プロジェクトとの適合性に基づいて評価されます。'],
|
||||
['id' => 'Audisi tahap awal dilakukan dari data dan media yang dikirim melalui sistem pendaftaran digital KEI.', 'jp' => '初期選考は、KEIのデジタル応募システムから送信されたデータとメディアをもとに行われます。'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'premium-production-standard',
|
||||
'date' => '02 Apr 2026',
|
||||
'title' => ['id' => 'Standar Produksi Premium untuk Proyek 2026', 'jp' => '2026年プロジェクト向けプレミアム制作基準'],
|
||||
'excerpt' => ['id' => 'KEI memperkuat workflow produksi untuk musik, live show, dan creative content agar lebih cepat serta konsisten.', 'jp' => 'KEIは、音楽・ライブ・クリエイティブコンテンツにおける制作フローを強化し、速度と一貫性を向上させました。'],
|
||||
'body' => [
|
||||
['id' => 'Tim KEI menyelaraskan workflow antar divisi agar pengembangan konsep, produksi konten, dan aktivasi lapangan dapat berjalan lebih efisien.', 'jp' => 'KEIチームは、企画開発・コンテンツ制作・現場運用をより効率化するため、部門横断のワークフローを統一しました。'],
|
||||
['id' => 'Pendekatan baru ini menempatkan kualitas visual, ketepatan eksekusi, dan pengalaman mitra sebagai prioritas utama.', 'jp' => 'この新しいアプローチでは、ビジュアル品質、実行精度、パートナー体験を最優先に据えています。'],
|
||||
['id' => 'Dengan struktur yang lebih rapi, KEI dapat mempercepat onboarding talent dan kolaborator untuk proyek baru.', 'jp' => 'より整理された構造により、KEIは新規プロジェクト向けのタレントや協業パートナーのオンボーディングを加速できます。'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_news_by_slug(string $slug): ?array
|
||||
{
|
||||
foreach (kei_news() as $article) {
|
||||
if ($article['slug'] === $slug) {
|
||||
return $article;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function kei_categories(): array
|
||||
{
|
||||
return [
|
||||
'Penyanyi' => ['id' => 'Penyanyi', 'jp' => 'シンガー'],
|
||||
'Talent' => ['id' => 'Talent', 'jp' => 'タレント'],
|
||||
'Kreator' => ['id' => 'Kreator', 'jp' => 'クリエイター'],
|
||||
];
|
||||
}
|
||||
|
||||
function kei_status_options(): array
|
||||
{
|
||||
return [
|
||||
'review' => ['id' => 'Review', 'jp' => '審査中'],
|
||||
'accepted' => ['id' => 'Accepted', 'jp' => '承認'],
|
||||
'rejected' => ['id' => 'Rejected', 'jp' => '不採用'],
|
||||
];
|
||||
}
|
||||
308
index.php
308
index.php
@ -1,150 +1,166 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$services = kei_services();
|
||||
$projects = array_slice(kei_projects(), 0, 4);
|
||||
$newsItems = kei_news();
|
||||
$stats = kei_stats();
|
||||
|
||||
render_head('Home', 'Official corporate website KOBA Entertainment Indonesia with multilingual company profile, services, projects, news, and talent registration workflow.');
|
||||
render_header('home');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
<main>
|
||||
<section class="hero section-space d-flex align-items-center">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="row g-5 align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Official Website · Indonesia × Japan', 'jp' => '公式サイト · インドネシア × 日本']) ?>>Official Website · Indonesia × Japan</span>
|
||||
<h1 class="display-title mt-3"<?= copy_attrs(['id' => 'KOBA Entertainment Indonesia', 'jp' => 'KOBA Entertainment Indonesia']) ?>>KOBA Entertainment Indonesia</h1>
|
||||
<p class="lead-copy"<?= copy_attrs(['id' => 'Building Global Entertainment from Indonesia', 'jp' => 'インドネシアから世界水準のエンターテインメントを築く']) ?>>Building Global Entertainment from Indonesia</p>
|
||||
<p class="support-copy"<?= copy_attrs(['id' => 'KOBA Entertainment Indonesia (KEI) adalah perusahaan musik dan entertainment yang menghubungkan Indonesia dan Jepang melalui kreativitas dan kolaborasi global.', 'jp' => 'KOBA Entertainment Indonesia(KEI)は、創造性と国際コラボレーションを通じてインドネシアと日本をつなぐ音楽・エンターテインメント企業です。']) ?>>KOBA Entertainment Indonesia (KEI) adalah perusahaan musik dan entertainment yang menghubungkan Indonesia dan Jepang melalui kreativitas dan kolaborasi global.</p>
|
||||
<div class="d-flex flex-wrap gap-3 mt-4">
|
||||
<a href="register.php" class="btn btn-brand btn-lg"<?= copy_attrs(['id' => 'Daftar Sekarang', 'jp' => '今すぐ応募']) ?>>Daftar Sekarang</a>
|
||||
<a href="projects.php" class="btn btn-outline-dark btn-lg"<?= copy_attrs(['id' => 'Explore', 'jp' => '詳しく見る']) ?>>Explore</a>
|
||||
</div>
|
||||
<div class="hero-stats mt-5">
|
||||
<?php foreach ($stats as $stat): ?>
|
||||
<div class="stat-chip">
|
||||
<strong><?= h($stat['number']) ?></strong>
|
||||
<span<?= copy_attrs($stat['label']) ?>><?= h(copy_text($stat['label'])) ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="hero-stage">
|
||||
<div class="hero-stage-main">
|
||||
<span class="stage-kicker"<?= copy_attrs(['id' => 'Cinematic Brand Direction', 'jp' => 'シネマティック・ブランド演出']) ?>>Cinematic Brand Direction</span>
|
||||
<h2<?= copy_attrs(['id' => 'Premium production with disciplined execution.', 'jp' => '規律ある実行力で、プレミアムな制作を。']) ?>>Premium production with disciplined execution.</h2>
|
||||
<p<?= copy_attrs(['id' => 'KEI menyatukan produksi musik, management talent, live experience, dan content strategy dalam satu ekosistem modern.', 'jp' => 'KEIは、音楽制作、タレントマネジメント、ライブ体験、コンテンツ戦略をひとつの現代的エコシステムに統合します。']) ?>>KEI menyatukan produksi musik, management talent, live experience, dan content strategy dalam satu ekosistem modern.</p>
|
||||
</div>
|
||||
<div class="hero-stage-grid">
|
||||
<article class="mini-panel">
|
||||
<span>01</span>
|
||||
<strong<?= copy_attrs(['id' => 'Music', 'jp' => '音楽']) ?>>Music</strong>
|
||||
<p<?= copy_attrs(['id' => 'Direction, recording, release.', 'jp' => '演出・収録・リリース。']) ?>>Direction, recording, release.</p>
|
||||
</article>
|
||||
<article class="mini-panel">
|
||||
<span>02</span>
|
||||
<strong<?= copy_attrs(['id' => 'Talent', 'jp' => 'タレント']) ?>>Talent</strong>
|
||||
<p<?= copy_attrs(['id' => 'Curated growth and positioning.', 'jp' => '選抜型の育成とポジショニング。']) ?>>Curated growth and positioning.</p>
|
||||
</article>
|
||||
<article class="mini-panel">
|
||||
<span>03</span>
|
||||
<strong<?= copy_attrs(['id' => 'Live', 'jp' => 'ライブ']) ?>>Live</strong>
|
||||
<p<?= copy_attrs(['id' => 'Show design and audience experience.', 'jp' => 'ショー設計と観客体験。']) ?>>Show design and audience experience.</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-space border-top-soft">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'About KEI', 'jp' => 'KEIについて']) ?>>About KEI</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'A creative bridge between Indonesia and Japan.', 'jp' => 'インドネシアと日本をつなぐクリエイティブブリッジ。']) ?>>A creative bridge between Indonesia and Japan.</h2>
|
||||
<p class="section-copy"<?= copy_attrs(['id' => 'KEI adalah perusahaan hiburan berbasis di Indonesia yang menghubungkan industri kreatif Jepang dan Indonesia melalui manajemen talenta, produksi, dan kolaborasi global.', 'jp' => 'KEIは、タレントマネジメント、制作、グローバルコラボレーションを通じて、日本とインドネシアのクリエイティブ産業を結ぶインドネシア拠点のエンターテインメント企業です。']) ?>>KEI adalah perusahaan hiburan berbasis di Indonesia yang menghubungkan industri kreatif Jepang dan Indonesia melalui manajemen talenta, produksi, dan kolaborasi global.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-space section-alt">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="section-heading-row">
|
||||
<div>
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Layanan', 'jp' => 'サービス']) ?>>Layanan</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'Integrated entertainment solutions with international discipline.', 'jp' => '国際水準の運営で統合型エンターテインメントを提供。']) ?>>Integrated entertainment solutions with international discipline.</h2>
|
||||
</div>
|
||||
<a href="services.php" class="text-link"<?= copy_attrs(['id' => 'Lihat Semua', 'jp' => 'すべて見る']) ?>>Lihat Semua</a>
|
||||
</div>
|
||||
<div class="row g-4 mt-2">
|
||||
<?php foreach ($services as $service): ?>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<article class="service-card h-100">
|
||||
<span class="service-code"><?= h($service['code']) ?></span>
|
||||
<h3<?= copy_attrs($service['title']) ?>><?= h(copy_text($service['title'])) ?></h3>
|
||||
<p<?= copy_attrs($service['description']) ?>><?= h(copy_text($service['description'])) ?></p>
|
||||
<small<?= copy_attrs($service['benefit']) ?>><?= h(copy_text($service['benefit'])) ?></small>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-space">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="section-heading-row">
|
||||
<div>
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Proyek', 'jp' => 'プロジェクト']) ?>>Proyek</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'Portfolio with strong visual direction and clear cultural story.', 'jp' => '強いビジュアル演出と文化的ストーリーを備えたポートフォリオ。']) ?>>Portfolio with strong visual direction and clear cultural story.</h2>
|
||||
</div>
|
||||
<a href="projects.php" class="text-link"<?= copy_attrs(['id' => 'Explore Projects', 'jp' => 'プロジェクトを見る']) ?>>Explore Projects</a>
|
||||
</div>
|
||||
<div class="row g-4 mt-2">
|
||||
<?php foreach ($projects as $project): ?>
|
||||
<div class="col-md-6">
|
||||
<article class="project-card h-100">
|
||||
<span class="project-category"<?= copy_attrs($project['category']) ?>><?= h(copy_text($project['category'])) ?></span>
|
||||
<h3<?= copy_attrs($project['title']) ?>><?= h(copy_text($project['title'])) ?></h3>
|
||||
<p<?= copy_attrs($project['summary']) ?>><?= h(copy_text($project['summary'])) ?></p>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-space cta-band">
|
||||
<div class="container-fluid kei-container narrow-copy text-center">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Talent Conversion', 'jp' => 'タレント募集']) ?>>Talent Conversion</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'Mulai karier Anda bersama KOBA Entertainment.', 'jp' => 'KOBA Entertainmentとともにキャリアを始めましょう。']) ?>>Mulai karier Anda bersama KOBA Entertainment.</h2>
|
||||
<p class="section-copy"<?= copy_attrs(['id' => 'Halaman Daftar sudah siap menerima pendaftaran singer, talent, dan kreator beserta media pendukung untuk tahap review awal.', 'jp' => '応募ページでは、シンガー・タレント・クリエイターの登録と、初期審査用メディアの送信に対応しています。']) ?>>Halaman Daftar sudah siap menerima pendaftaran singer, talent, dan kreator beserta media pendukung untuk tahap review awal.</p>
|
||||
<a href="register.php" class="btn btn-brand btn-lg mt-2"<?= copy_attrs(['id' => 'Daftar Sekarang', 'jp' => '今すぐ応募']) ?>>Daftar Sekarang</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-space section-alt">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="section-heading-row">
|
||||
<div>
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Berita', 'jp' => 'ニュース']) ?>>Berita</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'Latest updates from KOBA Entertainment Indonesia.', 'jp' => 'KOBA Entertainment Indonesiaの最新情報。']) ?>>Latest updates from KOBA Entertainment Indonesia.</h2>
|
||||
</div>
|
||||
<a href="news.php" class="text-link"<?= copy_attrs(['id' => 'Semua Berita', 'jp' => 'ニュース一覧']) ?>>Semua Berita</a>
|
||||
</div>
|
||||
<div class="row g-4 mt-2">
|
||||
<?php foreach ($newsItems as $article): ?>
|
||||
<div class="col-md-4">
|
||||
<article class="news-card h-100">
|
||||
<span class="news-date"><?= h($article['date']) ?></span>
|
||||
<h3<?= copy_attrs($article['title']) ?>><?= h(copy_text($article['title'])) ?></h3>
|
||||
<p<?= copy_attrs($article['excerpt']) ?>><?= h(copy_text($article['excerpt'])) ?></p>
|
||||
<a href="article.php?slug=<?= urlencode($article['slug']) ?>" class="text-link"<?= copy_attrs(['id' => 'Baca Detail', 'jp' => '詳細を見る']) ?>>Baca Detail</a>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-space">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="contact-strip">
|
||||
<div>
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Contact', 'jp' => 'お問い合わせ']) ?>>Contact</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'Connect with KEI for project, partnership, and talent discussion.', 'jp' => 'プロジェクト、提携、タレント相談はKEIへ。']) ?>>Connect with KEI for project, partnership, and talent discussion.</h2>
|
||||
<p class="section-copy">hello@kobaentertainment.id</p>
|
||||
</div>
|
||||
<a href="contact.php" class="btn btn-outline-dark"<?= copy_attrs(['id' => 'Hubungi Kami', 'jp' => 'お問い合わせ']) ?>>Hubungi Kami</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
|
||||
33
news.php
Normal file
33
news.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$newsItems = kei_news();
|
||||
render_head('Berita', 'Latest company updates from KOBA Entertainment Indonesia.');
|
||||
render_header('news');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Berita', 'jp' => 'ニュース']) ?>>Berita</span>
|
||||
<h1 class="page-title"<?= copy_attrs(['id' => 'Update terbaru KOBA Entertainment.', 'jp' => 'KOBA Entertainmentの最新情報。']) ?>>Update terbaru KOBA Entertainment.</h1>
|
||||
<p class="page-copy"<?= copy_attrs(['id' => 'Ringkasan artikel berikut memberi konteks tentang program, standard produksi, dan momentum kolaborasi KEI.', 'jp' => '以下の記事は、KEIのプログラム、制作基準、協業の動きを要約しています。']) ?>>Ringkasan artikel berikut memberi konteks tentang program, standard produksi, dan momentum kolaborasi KEI.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="row g-4">
|
||||
<?php foreach ($newsItems as $article): ?>
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<article class="news-card h-100">
|
||||
<span class="news-date"><?= h($article['date']) ?></span>
|
||||
<h2 class="h4"<?= copy_attrs($article['title']) ?>><?= h(copy_text($article['title'])) ?></h2>
|
||||
<p<?= copy_attrs($article['excerpt']) ?>><?= h(copy_text($article['excerpt'])) ?></p>
|
||||
<a href="article.php?slug=<?= urlencode($article['slug']) ?>" class="text-link"<?= copy_attrs(['id' => 'Baca Detail', 'jp' => '詳細を見る']) ?>>Baca Detail</a>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
80
profile.php
Normal file
80
profile.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$values = kei_values();
|
||||
render_head('Profil', 'Company profile, philosophy, and leadership message of KOBA Entertainment Indonesia.');
|
||||
render_header('profile');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Profil', 'jp' => 'プロフィール']) ?>>Profil</span>
|
||||
<h1 class="page-title"<?= copy_attrs(['id' => 'A premium entertainment company connecting Indonesia and Japan.', 'jp' => 'インドネシアと日本をつなぐプレミアム・エンターテインメント企業。']) ?>>A premium entertainment company connecting Indonesia and Japan.</h1>
|
||||
<p class="page-copy"<?= copy_attrs(['id' => 'KEI lahir sebagai jembatan kreatif yang membangun ekosistem berkelanjutan bagi artis, partner, dan audience global.', 'jp' => 'KEIは、アーティスト、パートナー、グローバルオーディエンスのために持続可能なエコシステムを築くクリエイティブブリッジとして誕生しました。']) ?>>KEI lahir sebagai jembatan kreatif yang membangun ekosistem berkelanjutan bagi artis, partner, dan audience global.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-6">
|
||||
<article class="quote-card h-100">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'CEO Greetings', 'jp' => 'CEOメッセージ']) ?>>CEO Greetings</span>
|
||||
<h2<?= copy_attrs(['id' => 'Direktur – Lisa Heriyati', 'jp' => 'ディレクター – Lisa Heriyati']) ?>>Direktur – Lisa Heriyati</h2>
|
||||
<p<?= copy_attrs(['id' => 'KOBA Entertainment Indonesia (KEI) lahir sebagai jembatan kreatif yang menghubungkan Indonesia dan dunia. Melalui hiburan, KEI menjadi bahasa universal yang melampaui budaya dan batas negara serta menyatukan masyarakat. KEI juga membangun ekosistem berkelanjutan bagi artis dan mitra untuk tumbuh bersama.', 'jp' => 'KOBA Entertainment Indonesia(KEI)は、インドネシアと世界をつなぐクリエイティブな架け橋として誕生しました。エンターテインメントを通じて、文化や国境を越える普遍的な言語となり、人々を結びます。KEIはまた、アーティストとパートナーがともに成長できる持続可能なエコシステムを築きます。']) ?>>KOBA Entertainment Indonesia (KEI) lahir sebagai jembatan kreatif yang menghubungkan Indonesia dan dunia. Melalui hiburan, KEI menjadi bahasa universal yang melampaui budaya dan batas negara serta menyatukan masyarakat. KEI juga membangun ekosistem berkelanjutan bagi artis dan mitra untuk tumbuh bersama.</p>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<article class="quote-card h-100">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'CEO Greetings', 'jp' => 'CEOメッセージ']) ?>>CEO Greetings</span>
|
||||
<h2<?= copy_attrs(['id' => 'CEO – Koki Ota', 'jp' => 'CEO – Koki Ota']) ?>>CEO – Koki Ota</h2>
|
||||
<p<?= copy_attrs(['id' => 'KEI bertujuan menjadi jembatan antara kreativitas Jepang dan semangat Indonesia, menciptakan nilai baru dari perpaduan budaya. Bersama artis dan mitra, kami membangun masa depan industri hiburan.', 'jp' => 'KEIは、日本の創造性とインドネシアの情熱を結びつけ、文化の融合から新しい価値を生み出すことを目指しています。アーティストとパートナーとともに、エンターテインメント産業の未来を築きます。']) ?>>KEI bertujuan menjadi jembatan antara kreativitas Jepang dan semangat Indonesia, menciptakan nilai baru dari perpaduan budaya. Bersama artis dan mitra, kami membangun masa depan industri hiburan.</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="row g-5 align-items-start">
|
||||
<div class="col-lg-6">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Tentang Perusahaan', 'jp' => '会社概要']) ?>>Tentang Perusahaan</span>
|
||||
<h2 class="section-title"<?= copy_attrs(['id' => 'KEI adalah perusahaan hiburan berbasis di Indonesia dengan perspektif internasional.', 'jp' => 'KEIは国際的な視点を持つインドネシア拠点のエンターテインメント企業です。']) ?>>KEI adalah perusahaan hiburan berbasis di Indonesia dengan perspektif internasional.</h2>
|
||||
<p class="section-copy"<?= copy_attrs(['id' => 'Kami menghubungkan industri kreatif Jepang dan Indonesia melalui manajemen talenta, produksi, dan kolaborasi global dengan kualitas yang konsisten.', 'jp' => '私たちは、タレントマネジメント、制作、グローバルコラボレーションを通じて、日本とインドネシアのクリエイティブ産業を結び、安定した品質を提供します。']) ?>>Kami menghubungkan industri kreatif Jepang dan Indonesia melalui manajemen talenta, produksi, dan kolaborasi global dengan kualitas yang konsisten.</p>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="philosophy-card">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Brand Philosophy', 'jp' => 'ブランド哲学']) ?>>Brand Philosophy</span>
|
||||
<h3>CHIGAI! <span<?= copy_attrs(['id' => '(PERBEDAAN)', 'jp' => '(違い)']) ?>>(PERBEDAAN)</span></h3>
|
||||
<ul class="bullet-list">
|
||||
<li<?= copy_attrs(['id' => 'Manusia, tempat, perasaan', 'jp' => '人、場所、感情']) ?>>Manusia, tempat, perasaan</li>
|
||||
<li<?= copy_attrs(['id' => 'Bahasa, agama, lingkungan', 'jp' => '言語、宗教、環境']) ?>>Bahasa, agama, lingkungan</li>
|
||||
<li<?= copy_attrs(['id' => 'Masa lalu dan masa depan', 'jp' => '過去と未来']) ?>>Masa lalu dan masa depan</li>
|
||||
</ul>
|
||||
<div class="mini-values">
|
||||
<span<?= copy_attrs(['id' => 'Mengenali Perbedaan', 'jp' => '違いを知る']) ?>>Mengenali Perbedaan</span>
|
||||
<span<?= copy_attrs(['id' => 'Menerima Perbedaan', 'jp' => '違いを受け入れる']) ?>>Menerima Perbedaan</span>
|
||||
<span<?= copy_attrs(['id' => 'Menantang Perbedaan', 'jp' => '違いに挑む']) ?>>Menantang Perbedaan</span>
|
||||
</div>
|
||||
<p class="mt-4 mb-0"<?= copy_attrs(['id' => 'Visi: WARNA BARU — menyatukan dua budaya unik untuk menciptakan identitas hiburan global yang tidak dapat ditiru.', 'jp' => 'ビジョン:WARNA BARU — 2つの独自文化を融合し、模倣できないグローバル・エンターテインメントのアイデンティティを創る。']) ?>>Visi: WARNA BARU — menyatukan dua budaya unik untuk menciptakan identitas hiburan global yang tidak dapat ditiru.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm section-alt">
|
||||
<div class="container-fluid kei-container">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Nilai Perusahaan', 'jp' => 'コアバリュー']) ?>>Nilai Perusahaan</span>
|
||||
<div class="row g-4 mt-2">
|
||||
<?php foreach ($values as $value): ?>
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<article class="value-card h-100">
|
||||
<h3<?= copy_attrs($value['title']) ?>><?= h(copy_text($value['title'])) ?></h3>
|
||||
<p<?= copy_attrs($value['body']) ?>><?= h(copy_text($value['body'])) ?></p>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
32
projects.php
Normal file
32
projects.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$projects = kei_projects();
|
||||
render_head('Proyek', 'Project portfolio of KOBA Entertainment Indonesia.');
|
||||
render_header('projects');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Proyek', 'jp' => 'プロジェクト']) ?>>Proyek</span>
|
||||
<h1 class="page-title"<?= copy_attrs(['id' => 'Portfolio karya KOBA Entertainment.', 'jp' => 'KOBA Entertainmentのポートフォリオ。']) ?>>Portfolio karya KOBA Entertainment.</h1>
|
||||
<p class="page-copy"<?= copy_attrs(['id' => 'Grid proyek ini menunjukkan bagaimana KEI memadukan live experience, visual storytelling, dan format kolaborasi modern.', 'jp' => 'このプロジェクトグリッドは、KEIがライブ体験、映像ストーリー、現代的な協業フォーマットをどのように融合しているかを示します。']) ?>>Grid proyek ini menunjukkan bagaimana KEI memadukan live experience, visual storytelling, dan format kolaborasi modern.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<div class="row g-4">
|
||||
<?php foreach ($projects as $project): ?>
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<article class="project-card tall h-100">
|
||||
<span class="project-category"<?= copy_attrs($project['category']) ?>><?= h(copy_text($project['category'])) ?></span>
|
||||
<h2 class="h4"<?= copy_attrs($project['title']) ?>><?= h(copy_text($project['title'])) ?></h2>
|
||||
<p<?= copy_attrs($project['summary']) ?>><?= h(copy_text($project['summary'])) ?></p>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
197
register.php
Normal file
197
register.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
|
||||
ensure_applications_table();
|
||||
$categories = kei_categories();
|
||||
$errors = [];
|
||||
$form = [
|
||||
'full_name' => trim((string) ($_POST['full_name'] ?? '')),
|
||||
'email' => trim((string) ($_POST['email'] ?? '')),
|
||||
'whatsapp' => trim((string) ($_POST['whatsapp'] ?? '')),
|
||||
'category' => trim((string) ($_POST['category'] ?? 'Penyanyi')),
|
||||
'consent_accuracy' => isset($_POST['consent_accuracy']),
|
||||
'consent_audition' => isset($_POST['consent_audition']),
|
||||
'consent_contract' => isset($_POST['consent_contract']),
|
||||
'consent_data' => isset($_POST['consent_data']),
|
||||
];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$form['whatsapp'] = normalize_whatsapp($form['whatsapp']);
|
||||
|
||||
if ($form['full_name'] === '' || mb_strlen($form['full_name']) < 3) {
|
||||
$errors[] = 'Nama lengkap minimal 3 karakter.';
|
||||
}
|
||||
if (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Format email belum valid.';
|
||||
}
|
||||
if ($form['whatsapp'] === '' || mb_strlen($form['whatsapp']) < 8) {
|
||||
$errors[] = 'Nomor WhatsApp belum valid.';
|
||||
}
|
||||
if (!array_key_exists($form['category'], $categories)) {
|
||||
$errors[] = 'Kategori pendaftaran tidak tersedia.';
|
||||
}
|
||||
if (!$form['consent_accuracy'] || !$form['consent_audition'] || !$form['consent_contract'] || !$form['consent_data']) {
|
||||
$errors[] = 'Semua persetujuan wajib dicentang.';
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
try {
|
||||
$photoUrl = handle_optional_upload('photo', [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/webp' => 'webp',
|
||||
], 4 * 1024 * 1024, 'photos');
|
||||
|
||||
$videoUrl = handle_optional_upload('video', [
|
||||
'video/mp4' => 'mp4',
|
||||
'video/quicktime' => 'mov',
|
||||
'video/webm' => 'webm',
|
||||
], 8 * 1024 * 1024, 'videos');
|
||||
|
||||
$stmt = db()->prepare('INSERT INTO talent_applications (full_name, email, whatsapp, category, photo_url, video_url, consent_accuracy, consent_audition, consent_contract, consent_data, status) VALUES (:full_name, :email, :whatsapp, :category, :photo_url, :video_url, :consent_accuracy, :consent_audition, :consent_contract, :consent_data, :status)');
|
||||
$stmt->execute([
|
||||
':full_name' => $form['full_name'],
|
||||
':email' => $form['email'],
|
||||
':whatsapp' => $form['whatsapp'],
|
||||
':category' => $form['category'],
|
||||
':photo_url' => $photoUrl,
|
||||
':video_url' => $videoUrl,
|
||||
':consent_accuracy' => $form['consent_accuracy'] ? 1 : 0,
|
||||
':consent_audition' => $form['consent_audition'] ? 1 : 0,
|
||||
':consent_contract' => $form['consent_contract'] ? 1 : 0,
|
||||
':consent_data' => $form['consent_data'] ? 1 : 0,
|
||||
':status' => 'review',
|
||||
]);
|
||||
|
||||
flash_set('register_success', [
|
||||
'id' => (int) db()->lastInsertId(),
|
||||
'full_name' => $form['full_name'],
|
||||
'email' => $form['email'],
|
||||
'category' => $form['category'],
|
||||
'submitted_at' => gmdate('d M Y H:i') . ' UTC',
|
||||
]);
|
||||
header('Location: register.php?success=1');
|
||||
exit;
|
||||
} catch (Throwable $exception) {
|
||||
$errors[] = 'Pendaftaran belum dapat dikirim. Silakan coba lagi.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$success = isset($_GET['success']) ? flash_get('register_success') : null;
|
||||
render_head('Daftar', 'Talent registration page for KOBA Entertainment Indonesia.');
|
||||
render_header('register');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Daftar', 'jp' => '応募']) ?>>Daftar</span>
|
||||
<h1 class="page-title"<?= copy_attrs(['id' => 'Bergabunglah bersama KOBA Entertainment.', 'jp' => 'KOBA Entertainmentに参加しませんか。']) ?>>Bergabunglah bersama KOBA Entertainment.</h1>
|
||||
<p class="page-copy"<?= copy_attrs(['id' => 'Kirim profil Anda untuk singer, talent, atau kreator. Tim KEI akan meninjau data dan media pendukung sebagai tahap awal audisi atau kolaborasi.', 'jp' => 'シンガー、タレント、クリエイターとしてプロフィールを送信してください。KEIチームが応募データと補足メディアを確認し、オーディションまたは協業の初期選考を行います。']) ?>>Kirim profil Anda untuk singer, talent, atau kreator. Tim KEI akan meninjau data dan media pendukung sebagai tahap awal audisi atau kolaborasi.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success border-0 soft-alert mb-4" role="alert">
|
||||
<strong<?= copy_attrs(['id' => 'Terima kasih telah mendaftar.', 'jp' => 'ご応募ありがとうございます。']) ?>>Terima kasih telah mendaftar.</strong>
|
||||
<div class="mt-2">
|
||||
<span><?= h($success['full_name']) ?></span>
|
||||
<span class="mx-2">•</span>
|
||||
<span>#<?= h((string) $success['id']) ?></span>
|
||||
<span class="mx-2">•</span>
|
||||
<span><?= h($success['submitted_at']) ?></span>
|
||||
</div>
|
||||
<p class="mb-0 mt-2"<?= copy_attrs(['id' => 'Data Anda masuk ke tahap review awal. Tim KEI akan menghubungi kandidat yang sesuai.', 'jp' => '応募データは初期審査に入りました。条件に合う候補者へKEIチームから連絡します。']) ?>>Data Anda masuk ke tahap review awal. Tim KEI akan menghubungi kandidat yang sesuai.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($errors): ?>
|
||||
<div class="alert alert-danger border-0 soft-alert mb-4" role="alert">
|
||||
<strong>Periksa kembali isian form.</strong>
|
||||
<ul class="mb-0 mt-2 ps-3">
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<li><?= h($error) ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row g-4 align-items-start">
|
||||
<div class="col-lg-7">
|
||||
<form class="app-card form-stack" method="post" enctype="multipart/form-data" data-submit-state>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="full_name" class="form-label"<?= copy_attrs(['id' => 'Nama Lengkap', 'jp' => '氏名']) ?>>Nama Lengkap</label>
|
||||
<input type="text" class="form-control" id="full_name" name="full_name" value="<?= h($form['full_name']) ?>" required<?= placeholder_attrs(['id' => 'Masukkan nama lengkap', 'jp' => '氏名を入力']) ?>>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="email" class="form-label"<?= copy_attrs(['id' => 'Email', 'jp' => 'メール']) ?>>Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="<?= h($form['email']) ?>" required<?= placeholder_attrs(['id' => 'nama@email.com', 'jp' => 'name@email.com']) ?>>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="whatsapp" class="form-label"<?= copy_attrs(['id' => 'WhatsApp', 'jp' => 'WhatsApp']) ?>>WhatsApp</label>
|
||||
<input type="text" class="form-control" id="whatsapp" name="whatsapp" value="<?= h($form['whatsapp']) ?>" required<?= placeholder_attrs(['id' => '08xxxxxxxxxx', 'jp' => '連絡先番号']) ?>>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="category" class="form-label"<?= copy_attrs(['id' => 'Kategori', 'jp' => 'カテゴリー']) ?>>Kategori</label>
|
||||
<select class="form-select" id="category" name="category" required>
|
||||
<?php foreach ($categories as $value => $label): ?>
|
||||
<option value="<?= h($value) ?>"<?= $form['category'] === $value ? ' selected' : '' ?><?= copy_attrs($label) ?>><?= h(copy_text($label)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="photo" class="form-label"<?= copy_attrs(['id' => 'Foto (opsional)', 'jp' => '写真(任意)']) ?>>Foto (opsional)</label>
|
||||
<input class="form-control" type="file" id="photo" name="photo" accept=".jpg,.jpeg,.png,.webp" data-file-target="photo-file-name">
|
||||
<small class="form-hint" id="photo-file-name">JPG, PNG, WEBP · max 4MB</small>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="video" class="form-label"<?= copy_attrs(['id' => 'Video (opsional)', 'jp' => '動画(任意)']) ?>>Video (opsional)</label>
|
||||
<input class="form-control" type="file" id="video" name="video" accept=".mp4,.mov,.webm" data-file-target="video-file-name">
|
||||
<small class="form-hint" id="video-file-name">MP4, MOV, WEBM · max 8MB</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="consent-list mt-4">
|
||||
<label class="form-check consent-item">
|
||||
<input class="form-check-input" type="checkbox" name="consent_accuracy"<?= $form['consent_accuracy'] ? ' checked' : '' ?>>
|
||||
<span class="form-check-label"<?= copy_attrs(['id' => 'Data yang saya kirim benar.', 'jp' => '送信する情報は正確です。']) ?>>Data yang saya kirim benar.</span>
|
||||
</label>
|
||||
<label class="form-check consent-item">
|
||||
<input class="form-check-input" type="checkbox" name="consent_audition"<?= $form['consent_audition'] ? ' checked' : '' ?>>
|
||||
<span class="form-check-label"<?= copy_attrs(['id' => 'Saya bersedia mengikuti audisi.', 'jp' => 'オーディション参加に同意します。']) ?>>Saya bersedia mengikuti audisi.</span>
|
||||
</label>
|
||||
<label class="form-check consent-item">
|
||||
<input class="form-check-input" type="checkbox" name="consent_contract"<?= $form['consent_contract'] ? ' checked' : '' ?>>
|
||||
<span class="form-check-label"<?= copy_attrs(['id' => 'Saya bersedia mengikuti proses kontrak bila lolos.', 'jp' => '通過した場合、契約プロセスに同意します。']) ?>>Saya bersedia mengikuti proses kontrak bila lolos.</span>
|
||||
</label>
|
||||
<label class="form-check consent-item">
|
||||
<input class="form-check-input" type="checkbox" name="consent_data"<?= $form['consent_data'] ? ' checked' : '' ?>>
|
||||
<span class="form-check-label"<?= copy_attrs(['id' => 'Saya memberi izin penggunaan data untuk keperluan review.', 'jp' => '審査目的でのデータ利用に同意します。']) ?>>Saya memberi izin penggunaan data untuk keperluan review.</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-brand btn-lg mt-4" data-submit-label="Kirim Pendaftaran"<?= copy_attrs(['id' => 'Kirim Pendaftaran', 'jp' => '応募を送信']) ?>>Kirim Pendaftaran</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<aside class="app-card info-stack">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Workflow', 'jp' => 'ワークフロー']) ?>>Workflow</span>
|
||||
<h2<?= copy_attrs(['id' => 'Alur pendaftaran yang sederhana, rapi, dan siap direview.', 'jp' => 'シンプルで整理された、レビューしやすい応募フロー。']) ?>>Alur pendaftaran yang sederhana, rapi, dan siap direview.</h2>
|
||||
<ol class="ordered-list">
|
||||
<li<?= copy_attrs(['id' => 'Isi data talent dan pilih kategori.', 'jp' => 'プロフィール情報とカテゴリを入力。']) ?>>Isi data talent dan pilih kategori.</li>
|
||||
<li<?= copy_attrs(['id' => 'Upload foto/video pendukung bila tersedia.', 'jp' => '必要に応じて写真・動画を追加。']) ?>>Upload foto/video pendukung bila tersedia.</li>
|
||||
<li<?= copy_attrs(['id' => 'Dapatkan konfirmasi lalu data masuk ke dashboard admin.', 'jp' => '送信後、管理ダッシュボードで確認されます。']) ?>>Dapatkan konfirmasi lalu data masuk ke dashboard admin.</li>
|
||||
</ol>
|
||||
<div class="detail-panel mt-4">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Admin Review', 'jp' => '管理レビュー']) ?>>Admin Review</span>
|
||||
<p class="mb-2"<?= copy_attrs(['id' => 'Tim admin dapat login untuk mencari data, memfilter status, membuka detail kandidat, dan ekspor CSV.', 'jp' => '管理チームはログイン後、検索・ステータス絞り込み・候補者詳細確認・CSV書き出しが可能です。']) ?>>Tim admin dapat login untuk mencari data, memfilter status, membuka detail kandidat, dan ekspor CSV.</p>
|
||||
<a href="admin/login.php" class="text-link"<?= copy_attrs(['id' => 'Buka Admin Dashboard', 'jp' => '管理画面を開く']) ?>>Buka Admin Dashboard</a>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
38
services.php
Normal file
38
services.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$services = kei_services();
|
||||
render_head('Layanan', 'Integrated entertainment services from KOBA Entertainment Indonesia.');
|
||||
render_header('services');
|
||||
?>
|
||||
<main>
|
||||
<section class="page-hero section-space-sm">
|
||||
<div class="container-fluid kei-container narrow-copy">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Layanan', 'jp' => 'サービス']) ?>>Layanan</span>
|
||||
<h1 class="page-title"<?= copy_attrs(['id' => 'Integrated entertainment solutions with international standards.', 'jp' => '国際水準で提供する統合型エンターテインメントソリューション。']) ?>>Integrated entertainment solutions with international standards.</h1>
|
||||
<p class="page-copy"<?= copy_attrs(['id' => 'Setiap layanan KEI dirancang untuk menghasilkan kualitas tinggi, pengalaman premium, dan eksekusi yang konsisten.', 'jp' => 'KEIの各サービスは、高品質、プレミアムな体験、一貫した実行力を実現するよう設計されています。']) ?>>Setiap layanan KEI dirancang untuk menghasilkan kualitas tinggi, pengalaman premium, dan eksekusi yang konsisten.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section-space-sm border-top-soft">
|
||||
<div class="container-fluid kei-container">
|
||||
<?php foreach ($services as $index => $service): ?>
|
||||
<article class="service-detail <?= $index < count($services) - 1 ? 'with-border' : '' ?>">
|
||||
<div>
|
||||
<span class="service-code large"><?= h($service['code']) ?></span>
|
||||
</div>
|
||||
<div>
|
||||
<h2<?= copy_attrs($service['title']) ?>><?= h(copy_text($service['title'])) ?></h2>
|
||||
<p<?= copy_attrs($service['description']) ?>><?= h(copy_text($service['description'])) ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="detail-panel">
|
||||
<span class="eyebrow"<?= copy_attrs(['id' => 'Manfaat', 'jp' => 'ベネフィット']) ?>>Manfaat</span>
|
||||
<p class="mb-0"<?= copy_attrs($service['benefit']) ?>><?= h(copy_text($service['benefit'])) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<?php render_footer(); ?>
|
||||
Loading…
x
Reference in New Issue
Block a user