DONE: add portal setting endpoint for admin to update
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\Admin\PortalSettings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PortalSetting;
|
||||
|
||||
class GetController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
$setting = PortalSetting::singleton();
|
||||
$defaults = PortalSetting::defaults();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'home_mat_label' => $setting->home_mat_label ?? $defaults['home_mat_label'],
|
||||
'home_banner_image_path' => $setting->home_banner_image_path ?? $defaults['home_banner_image_path'],
|
||||
'zoom_meeting_label' => $setting->zoom_meeting_label ?? $defaults['zoom_meeting_label'],
|
||||
'zoom_subtitle' => $setting->zoom_subtitle ?? $defaults['zoom_subtitle'],
|
||||
'zoom_image_url' => $setting->zoom_image_url ?? $defaults['zoom_image_url'],
|
||||
'zoom_meeting_url' => $setting->zoom_meeting_url ?? $defaults['zoom_meeting_url'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\Admin\PortalSettings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PortalSetting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UpdateController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$setting = PortalSetting::singleton();
|
||||
|
||||
$validated = $request->validate([
|
||||
'home_mat_label' => 'nullable|string|max:255',
|
||||
'zoom_meeting_label' => 'nullable|string|max:255',
|
||||
'zoom_subtitle' => 'nullable|string|max:2000',
|
||||
'zoom_image_url' => 'nullable|string|max:2048',
|
||||
'zoom_meeting_url' => 'nullable|string|max:2048',
|
||||
'home_banner_image' => 'nullable|file|mimes:jpg,jpeg,png,webp|max:5120',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('home_banner_image')) {
|
||||
$file = $request->file('home_banner_image');
|
||||
$ext = strtolower($file->getClientOriginalExtension() ?: 'jpg');
|
||||
$name = 'banner.' . $ext;
|
||||
$relativePath = 'portal/' . $name;
|
||||
Storage::disk('public')->putFileAs('portal', $file, $name);
|
||||
$validated['home_banner_image_path'] = '/storage/' . $relativePath;
|
||||
}
|
||||
|
||||
$setting->fill([
|
||||
'home_mat_label' => $validated['home_mat_label'] ?? $setting->home_mat_label,
|
||||
'home_banner_image_path' => $validated['home_banner_image_path'] ?? $setting->home_banner_image_path,
|
||||
'zoom_meeting_label' => $validated['zoom_meeting_label'] ?? $setting->zoom_meeting_label,
|
||||
'zoom_subtitle' => $validated['zoom_subtitle'] ?? $setting->zoom_subtitle,
|
||||
'zoom_image_url' => $validated['zoom_image_url'] ?? $setting->zoom_image_url,
|
||||
'zoom_meeting_url' => $validated['zoom_meeting_url'] ?? $setting->zoom_meeting_url,
|
||||
]);
|
||||
$setting->save();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Portal settings updated.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\v1\PortalSettings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PortalSetting;
|
||||
|
||||
class GetController extends Controller
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
$setting = PortalSetting::singleton();
|
||||
$defaults = PortalSetting::defaults();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'home_mat_label' => $setting->home_mat_label ?? $defaults['home_mat_label'],
|
||||
'home_banner_image_path' => $setting->home_banner_image_path ?? $defaults['home_banner_image_path'],
|
||||
'zoom_meeting_label' => $setting->zoom_meeting_label ?? $defaults['zoom_meeting_label'],
|
||||
'zoom_subtitle' => $setting->zoom_subtitle ?? $defaults['zoom_subtitle'],
|
||||
'zoom_image_url' => $setting->zoom_image_url ?? $defaults['zoom_image_url'],
|
||||
'zoom_meeting_url' => $setting->zoom_meeting_url ?? $defaults['zoom_meeting_url'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PortalSetting extends Model
|
||||
{
|
||||
protected $table = 'portal_settings';
|
||||
|
||||
protected $fillable = [
|
||||
'home_mat_label',
|
||||
'home_banner_image_path',
|
||||
'zoom_meeting_label',
|
||||
'zoom_subtitle',
|
||||
'zoom_image_url',
|
||||
'zoom_meeting_url',
|
||||
];
|
||||
|
||||
public static function defaults(): array
|
||||
{
|
||||
return [
|
||||
'home_mat_label' => 'MAT KE-29',
|
||||
'home_banner_image_path' => '/images/banner.jpeg',
|
||||
'zoom_meeting_label' => 'KoPKB KALI KE-29/2025',
|
||||
'zoom_subtitle' => 'Sertai Mesyuarat Agung Tahunan KoPKB kali ke-29/2025 melalui pautan rasmi di bawah.',
|
||||
'zoom_image_url' => 'https://th.bing.com/th/id/OIP.TGLvYXfe4xq5MTZe4UnTDgHaEy?w=255&h=180&c=7&r=0&o=5&dpr=1.8&pid=1.7',
|
||||
'zoom_meeting_url' => 'https://meet.google.com/paa-tpck-ssq',
|
||||
];
|
||||
}
|
||||
|
||||
public static function singleton(): self
|
||||
{
|
||||
$defaults = static::defaults();
|
||||
|
||||
/** @var self $record */
|
||||
$record = static::query()->first();
|
||||
if ($record) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
return static::query()->create($defaults);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePortalSettingsTable extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('portal_settings', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('home_mat_label')->nullable();
|
||||
$table->string('home_banner_image_path')->nullable();
|
||||
|
||||
$table->string('zoom_meeting_label')->nullable();
|
||||
$table->text('zoom_subtitle')->nullable();
|
||||
$table->string('zoom_image_url')->nullable();
|
||||
$table->string('zoom_meeting_url')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('portal_settings');
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -54,6 +54,10 @@
|
||||
<a href="#"><b>Log Aktiviti</b></a>
|
||||
</router-link>
|
||||
|
||||
<router-link :to="{ name: 'Tetapan Portal' }" v-if="data.user.role == 1" tag="li">
|
||||
<a href="#"><b>Tetapan Portal</b></a>
|
||||
</router-link>
|
||||
|
||||
|
||||
</ul>
|
||||
<ul class="nav navbar-right navbar-nav">
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="container col-md-10 col-md-offset-1">
|
||||
<h4>Tetapan Portal (MAT / Mesyuarat)</h4>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label>Label pautan (Home) – contoh: MAT KE-29</label>
|
||||
<input v-model="form.home_mat_label" type="text" class="form-control" placeholder="MAT KE-29">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Banner (Home)</label>
|
||||
<!-- <div v-if="form.home_banner_image_path" class="help-block">
|
||||
Semasa: <code>{{ form.home_banner_image_path }}</code>
|
||||
</div> -->
|
||||
<input type="file" class="form-control" @change="onBannerChange" accept="image/*">
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Label KoPKB (Zoom) – contoh: KoPKB KALI KE-29/2025</label>
|
||||
<input v-model="form.zoom_meeting_label" type="text" class="form-control" placeholder="KoPKB KALI KE-29/2025">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Ayat penerangan (Zoom)</label>
|
||||
<textarea v-model="form.zoom_subtitle" class="form-control" rows="3"
|
||||
placeholder="Sertai Mesyuarat Agung Tahunan..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Gambar</label>
|
||||
<!-- <div class="help-block">
|
||||
Guna path seperti <code>/storage/...</code> atau <code>/images/...</code>. Link luar
|
||||
(<code>https://...</code>) akan diabaikan.
|
||||
</div> -->
|
||||
<input v-model="form.zoom_image_url" type="text" class="form-control"
|
||||
placeholder="/storage/portal/meeting.png" disabled>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Pautan mesyuarat (Zoom) – Google Meet</label>
|
||||
<input v-model="form.zoom_meeting_url" type="text" class="form-control"
|
||||
placeholder="https://meet.google.com/...">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" :disabled="saving" @click="save">
|
||||
<i class="fa fa-save"></i> {{ saving ? 'Menyimpan...' : 'Simpan' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
bannerFile: null,
|
||||
form: {
|
||||
home_mat_label: "",
|
||||
home_banner_image_path: "",
|
||||
zoom_meeting_label: "",
|
||||
zoom_subtitle: "",
|
||||
zoom_image_url: "",
|
||||
zoom_meeting_url: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.util.setTitle("MyKoPKB - Tetapan Portal");
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onBannerChange(e) {
|
||||
const files = e && e.target ? e.target.files : null;
|
||||
this.bannerFile = files && files.length ? files[0] : null;
|
||||
},
|
||||
|
||||
async fetch() {
|
||||
this.loading = true;
|
||||
try {
|
||||
this.util.setAuthorization();
|
||||
const res = await axios.get(config.API + "admin/portal-settings");
|
||||
if (res && res.data && res.data.status === "success") {
|
||||
const d = res.data.data || {};
|
||||
this.form = {
|
||||
home_mat_label: d.home_mat_label || "",
|
||||
home_banner_image_path: d.home_banner_image_path || "",
|
||||
zoom_meeting_label: d.zoom_meeting_label || "",
|
||||
zoom_subtitle: d.zoom_subtitle || "",
|
||||
zoom_image_url: d.zoom_image_url || "",
|
||||
zoom_meeting_url: d.zoom_meeting_url || ""
|
||||
};
|
||||
} else {
|
||||
this.util.showResult(res, "error");
|
||||
}
|
||||
} catch (e) {
|
||||
this.util.showResult(e, "error");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async save() {
|
||||
if (this.saving) return;
|
||||
this.saving = true;
|
||||
try {
|
||||
this.util.setAuthorization();
|
||||
const fd = new FormData();
|
||||
fd.append("home_mat_label", this.form.home_mat_label || "");
|
||||
fd.append("zoom_meeting_label", this.form.zoom_meeting_label || "");
|
||||
fd.append("zoom_subtitle", this.form.zoom_subtitle || "");
|
||||
// zoom_image_url is intentionally not editable from UI (uses default icon / design).
|
||||
fd.append("zoom_meeting_url", this.form.zoom_meeting_url || "");
|
||||
if (this.bannerFile) fd.append("home_banner_image", this.bannerFile);
|
||||
|
||||
const res = await axios.post(config.API + "admin/portal-settings", fd, {
|
||||
headers: { "Content-Type": "multipart/form-data" }
|
||||
});
|
||||
|
||||
if (res && res.data && res.data.status === "success") {
|
||||
this.util.notify("Berjaya simpan tetapan portal.", "success");
|
||||
this.bannerFile = null;
|
||||
await this.fetch();
|
||||
} else {
|
||||
this.util.showResult(res, "error");
|
||||
}
|
||||
} catch (e) {
|
||||
this.util.showResult(e, "error");
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="home-section-container home-hero-section">
|
||||
<section class="home-hero-wrap">
|
||||
<div class="hero-banner home-hero-banner">
|
||||
<img src="/images/banner-mat28-new.jpg" alt="Banner" class="hero-banner-image">
|
||||
<img :src="bannerUrl" alt="Banner" class="hero-banner-image">
|
||||
<div class="home-hero-banner-shine" aria-hidden="true"></div>
|
||||
</div>
|
||||
<div class="hero-banner-caption home-hero-caption">
|
||||
@@ -192,11 +192,18 @@ export default {
|
||||
claimCode: "",
|
||||
claimExpiresAt: "",
|
||||
claimError: "",
|
||||
matLinkUrl: "https://linktr.ee/kopkb"
|
||||
matLinkUrl: "https://linktr.ee/kopkb",
|
||||
portalSettings: null,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
bannerUrl() {
|
||||
const p = this.portalSettings && this.portalSettings.home_banner_image_path
|
||||
? this.portalSettings.home_banner_image_path
|
||||
: "/images/banner.jpeg";
|
||||
return p;
|
||||
},
|
||||
/**
|
||||
* Maya: boleh mengundi selepas daftar kehadiran. Fizikal: perlu disahkan pentadbir dahulu.
|
||||
*/
|
||||
@@ -231,6 +238,9 @@ export default {
|
||||
const user = this.data?.user || {};
|
||||
const election = this.data?.election || {};
|
||||
const noAnggota = user.no_anggota;
|
||||
const matLabel = (this.portalSettings && this.portalSettings.home_mat_label)
|
||||
? this.portalSettings.home_mat_label
|
||||
: "MAT KE-29";
|
||||
const links = [
|
||||
{
|
||||
key: "calon",
|
||||
@@ -243,7 +253,7 @@ export default {
|
||||
},
|
||||
{
|
||||
key: "mat28",
|
||||
title: "MAT KE-28",
|
||||
title: matLabel,
|
||||
desc: "Pautan luar ke maklumat mesyuarat dan sumber berkaitan",
|
||||
icon: "fa-globe",
|
||||
href: this.matLinkUrl,
|
||||
@@ -339,6 +349,7 @@ export default {
|
||||
|
||||
mounted() {
|
||||
this.syncBarcodeFromUser();
|
||||
this.fetchPortalSettings();
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -348,6 +359,18 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchPortalSettings() {
|
||||
try {
|
||||
this.util.setAuthorization();
|
||||
const res = await axios.get(config.API + "portal-settings");
|
||||
if (res && res.data && res.data.status === "success") {
|
||||
this.portalSettings = res.data.data || null;
|
||||
}
|
||||
} catch (e) {
|
||||
// Non-critical — fall back to defaults.
|
||||
}
|
||||
},
|
||||
|
||||
syncBarcodeFromUser() {
|
||||
if (this.data?.user?.no_kp) {
|
||||
this.noKp = this.data.user.no_kp;
|
||||
|
||||
@@ -1,38 +1,35 @@
|
||||
<template>
|
||||
<div class="row col-md-10 col-md-offset-1 zoom-page">
|
||||
<div class="col-md-12">
|
||||
<div class="zoom-page">
|
||||
<div class="zoom-shell">
|
||||
<div class="zoom-hero-card">
|
||||
<div class="zoom-section-kicker">Pautan Mesyuarat</div>
|
||||
<h3 class="zoom-title">SELAMAT DATANG KE MESYUARAT AGUNG TAHUNAN</h3>
|
||||
<p class="zoom-subtitle">
|
||||
Sertai Mesyuarat Agung Tahunan KoPKB kali ke-28/2024 melalui pautan rasmi di bawah.
|
||||
</p>
|
||||
<h3 class="zoom-title">Selamat Datang</h3>
|
||||
<p class="zoom-subtitle">{{ zoomSubtitle }}</p>
|
||||
</div>
|
||||
|
||||
<div class="zoom-main-card">
|
||||
<div class="centered-container zoom-image-section">
|
||||
<div class="image-container zoom-image-card">
|
||||
<img :src="imageUrl" alt="meeting apps" class="zoom-image" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="zoom-meeting-badge">{{ zoomMeetingLabel }}</div>
|
||||
|
||||
<div class="centered-container zoom-content-section">
|
||||
<div class="zoom-meeting-badge">
|
||||
KoPKB KALI KE-28/2024
|
||||
</div>
|
||||
<h4 class="zoom-content-title">Google Meet Rasmi</h4>
|
||||
<p class="zoom-content-text">
|
||||
Pastikan anda telah mendaftar kehadiran dan bersedia dengan sambungan internet yang stabil sebelum menyertai
|
||||
mesyuarat.
|
||||
</p>
|
||||
<h4 class="zoom-content-title">Pautan Mesyuarat Rasmi</h4>
|
||||
<p class="zoom-content-text">
|
||||
Pastikan anda telah mendaftar kehadiran dan bersedia dengan sambungan internet yang stabil sebelum menyertai mesyuarat.
|
||||
</p>
|
||||
|
||||
<div class="zoom-action-panel">
|
||||
<button class="btn-interactive" @click="navigateToURL" type="button">
|
||||
<i class="fa fa-video-camera"></i> Klik pada pautan berikut
|
||||
</button>
|
||||
<div class="zoom-link-note">
|
||||
Pautan akan dibuka terus ke sesi mesyuarat rasmi.
|
||||
</div>
|
||||
<div class="zoom-action-panel">
|
||||
<button class="btn-interactive" @click="navigateToURL" type="button">
|
||||
<i class="fa fa-video-camera"></i> Buka pautan mesyuarat
|
||||
</button>
|
||||
<button class="btn-secondary" @click="copyMeetingUrl" type="button">
|
||||
<i class="fa fa-clipboard"></i> Salin pautan
|
||||
</button>
|
||||
|
||||
<div class="zoom-link-preview" v-if="meetingUrl">
|
||||
<span class="zoom-link-preview__label">Pautan:</span>
|
||||
<span class="zoom-link-preview__value">{{ meetingUrl }}</span>
|
||||
</div>
|
||||
|
||||
<div class="zoom-link-note">
|
||||
Pautan akan dibuka terus ke sesi mesyuarat rasmi.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,14 +41,73 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
imageUrl: 'https://th.bing.com/th/id/OIP.TGLvYXfe4xq5MTZe4UnTDgHaEy?w=255&h=180&c=7&r=0&o=5&dpr=1.8&pid=1.7',
|
||||
meetingUrl: 'https://meet.google.com/paa-tpck-ssq'
|
||||
meetingUrl: 'https://meet.google.com/paa-tpck-ssq',
|
||||
portalSettings: null,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
zoomMeetingLabel() {
|
||||
return (this.portalSettings && this.portalSettings.zoom_meeting_label)
|
||||
? this.portalSettings.zoom_meeting_label
|
||||
: "KoPKB KALI KE-29/2025";
|
||||
},
|
||||
zoomSubtitle() {
|
||||
return (this.portalSettings && this.portalSettings.zoom_subtitle)
|
||||
? this.portalSettings.zoom_subtitle
|
||||
: "Sertai Mesyuarat Agung Tahunan KoPKB kali ke-29/2025 melalui pautan rasmi di bawah.";
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchPortalSettings();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchPortalSettings() {
|
||||
try {
|
||||
this.util.setAuthorization();
|
||||
const res = await axios.get(config.API + "portal-settings");
|
||||
if (res && res.data && res.data.status === "success") {
|
||||
this.portalSettings = res.data.data || null;
|
||||
if (this.portalSettings.zoom_meeting_url) this.meetingUrl = this.portalSettings.zoom_meeting_url;
|
||||
}
|
||||
} catch (e) {
|
||||
// Non-critical — fall back to defaults.
|
||||
}
|
||||
},
|
||||
|
||||
navigateToURL() {
|
||||
window.open(this.meetingUrl, '_blank');
|
||||
},
|
||||
|
||||
async copyMeetingUrl() {
|
||||
const text = String(this.meetingUrl || "").trim();
|
||||
if (!text) return;
|
||||
try {
|
||||
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
this.util.notify("Pautan disalin.", "success");
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// fall through
|
||||
}
|
||||
|
||||
try {
|
||||
const el = document.createElement("textarea");
|
||||
el.value = text;
|
||||
el.setAttribute("readonly", "");
|
||||
el.style.position = "absolute";
|
||||
el.style.left = "-9999px";
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(el);
|
||||
this.util.notify("Pautan disalin.", "success");
|
||||
} catch (e) {
|
||||
this.util.notify("Tidak dapat salin pautan.", "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -65,6 +121,12 @@ export default {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.zoom-shell {
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.zoom-hero-card {
|
||||
margin-bottom: 22px;
|
||||
padding: 26px 28px;
|
||||
@@ -87,8 +149,8 @@ export default {
|
||||
.zoom-title {
|
||||
margin: 0 0 10px;
|
||||
color: #17324d;
|
||||
font-size: 2.8rem;
|
||||
font-weight: 700;
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
@@ -101,49 +163,19 @@ export default {
|
||||
}
|
||||
|
||||
.zoom-main-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
|
||||
gap: 24px;
|
||||
padding: 24px;
|
||||
padding: 26px 28px;
|
||||
border-radius: 22px;
|
||||
background: #fff;
|
||||
border: 1px solid #e2ebf5;
|
||||
box-shadow: 0 16px 34px rgba(35, 64, 97, 0.08);
|
||||
}
|
||||
|
||||
.centered-container {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 40vh;
|
||||
}
|
||||
|
||||
.zoom-image-card {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: 18px;
|
||||
border: 1px solid #dbe7f3;
|
||||
background: #f4f8fd;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.zoom-image {
|
||||
width: 100%;
|
||||
max-height: 420px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.zoom-content-section {
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.zoom-meeting-badge {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
background: #e8f1ff;
|
||||
color: #215db8;
|
||||
@@ -173,6 +205,10 @@ export default {
|
||||
background: linear-gradient(180deg, #f8fbff 0%, #eef6ff 100%);
|
||||
border: 1px solid #d9e8fb;
|
||||
box-shadow: 0 12px 28px rgba(35, 64, 97, 0.07);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-interactive {
|
||||
@@ -187,6 +223,7 @@ export default {
|
||||
transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease;
|
||||
outline: none;
|
||||
box-shadow: 0 12px 24px rgba(13, 110, 253, 0.25);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-interactive:hover {
|
||||
@@ -203,24 +240,66 @@ export default {
|
||||
box-shadow: 0 0 5px 2px rgba(0, 123, 255, 0.5);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 14px 18px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
color: #17324d;
|
||||
border: 1px solid rgba(23, 50, 77, 0.16);
|
||||
border-radius: 999px;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 10px 22px rgba(35, 64, 97, 0.08);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 14px 26px rgba(35, 64, 97, 0.12);
|
||||
}
|
||||
|
||||
.zoom-link-preview {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: baseline;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgba(33, 93, 184, 0.16);
|
||||
}
|
||||
|
||||
.zoom-link-preview__label {
|
||||
flex-shrink: 0;
|
||||
font-weight: 900;
|
||||
color: #215db8;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.zoom-link-preview__value {
|
||||
color: #17324d;
|
||||
font-weight: 700;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.zoom-link-note {
|
||||
grid-column: 1 / -1;
|
||||
margin-top: 12px;
|
||||
color: #5f7388;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.zoom-main-card {
|
||||
.zoom-action-panel {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.zoom-content-section {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
.zoom-hero-card,
|
||||
.zoom-main-card {
|
||||
padding: 18px;
|
||||
@@ -228,7 +307,7 @@ export default {
|
||||
}
|
||||
|
||||
.zoom-title {
|
||||
font-size: 2.2rem;
|
||||
font-size: 2.35rem;
|
||||
}
|
||||
|
||||
.zoom-subtitle,
|
||||
|
||||
Vendored
+7
@@ -60,6 +60,7 @@ import ManageBelianKomoditi from "./components/demo/admin/admin/beliankomoditi.v
|
||||
import ManageKontrakMurabahah from "./components/demo/admin/admin/kontrakmurabahah.vue";
|
||||
import CetakPermohonan from "./components/demo/admin/admin/cetakpermohonan.vue";
|
||||
import ManagePenyata from "./components/demo/admin/admin/penyata-anggota.vue";
|
||||
import AdminPortalSettings from "./components/demo/admin/portal-settings/index.vue";
|
||||
|
||||
const default_component = {
|
||||
template: "<div>Not found: {{ $route.path }}</div>",
|
||||
@@ -184,6 +185,12 @@ export default [
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: "portal-settings",
|
||||
component: AdminPortalSettings,
|
||||
name: "Tetapan Portal",
|
||||
},
|
||||
|
||||
{
|
||||
path: "position",
|
||||
component: ManagePosition,
|
||||
|
||||
@@ -36,6 +36,7 @@ Route::prefix('v1')->group(function () { //Version 1 of my Rest API
|
||||
Route::post('election/vote', 'API\v1\Election\VoteController')->middleware(['fizikal_registration_verified', 'isvoted']);
|
||||
Route::get('election/result', 'API\v1\Election\ResultController')->middleware('has_voted');
|
||||
Route::post('allowance/code', 'API\v1\Allowance\ClaimCodeController');
|
||||
Route::get('portal-settings', 'API\v1\PortalSettings\GetController');
|
||||
Route::post('voter/logout', 'API\v1\Voter\LogoutController');
|
||||
});
|
||||
|
||||
@@ -57,6 +58,8 @@ Route::prefix('v1')->group(function () { //Version 1 of my Rest API
|
||||
Route::get('information', 'API\v1\Admin\InformationController');
|
||||
Route::get('logout', 'API\v1\Admin\LogoutController');
|
||||
Route::get('activity-log', 'API\v1\Admin\ActivityLog\IndexController');
|
||||
Route::get('portal-settings', 'API\v1\Admin\PortalSettings\GetController');
|
||||
Route::post('portal-settings', 'API\v1\Admin\PortalSettings\UpdateController');
|
||||
Route::post('impersonate', 'API\v1\Admin\Impersonate\StartController')->middleware('main_admin');
|
||||
Route::post('impersonate/leave', 'API\v1\Admin\Impersonate\LeaveController');
|
||||
Route::get('{id}', 'API\v1\Admin\GetController@show');
|
||||
|
||||
Reference in New Issue
Block a user