takeone-youtube-clone/.claude/component-usage.md
ghassan 66fd78c10f Add multi-language audio tracks and self-hosted flag-icons
Introduce per-video language support and multiple audio tracks
(VideoAudioTrack model + migrations for language, description, title),
a reusable language-select component, and a track-editor form. Bundle
the self-hosted flag-icons v7.2.3 library and a NAS auto-sync command.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:32:52 +03:00

12 KiB
Raw Blame History

Reusable Select Component Usage

This file tracks every page/partial that uses <x-phone-code-select>, <x-country-select>, <x-timezone-select>, or <x-language-select>. Update this file whenever you add or remove a component from a view.

When modifying any component or its data source, check all pages in the relevant section below and verify the change works correctly in each context.


Data sources

app/Data/Countries.phpApp\Data\Countries

Method Used by component
Countries::forPhoneCode() <x-phone-code-select>
Countries::forCountry() <x-country-select>
Countries::forTimezone() <x-timezone-select>
Countries::all() All three (via the above methods)

Adding or renaming a field in Countries::all() requires updating the corresponding for*() method too.

app/Data/Languages.phpApp\Data\Languages

Method Used by component
Languages::forLanguage() <x-language-select>
Languages::all() Via forLanguage()

Arabic and English are pinned to the top of the list; all others are sorted alphabetically by English name. Stored value is the ISO 639-1 code (e.g. "ar", "en").


Shared CSS / JS

The .csd-* CSS rules and the window.CSD class are duplicated across all four component files inside @once guards. If you change the look or behaviour of the dropdown, update all four component files:

  • resources/views/components/phone-code-select.blade.php
  • resources/views/components/country-select.blade.php
  • resources/views/components/timezone-select.blade.php
  • resources/views/components/language-select.blade.php

The @once Blade directive ensures the browser only receives one copy of the CSS/JS even when multiple components are on the same page.

language-select also emits an extra @once('lsd-badge-styles') block for the .lsd-code ISO badge that appears in place of a flag emoji.


<x-video-insights>

File: resources/views/components/video-insights.blade.php
Props: :videoVideo model instance.
Behaviour: Renders the Insights tab panel (<div class="vdb-panel" id="vdb-insights">), the drill-down modal, all .ins-* CSS, and all insights JS (loadInsights, renderInsights, modal openers, country/day/downloader drill-downs). Only renders if Auth::id() === $video->user_id. Must be placed inside .vdb-wrap, after the About panel, so the tab-switch CSS applies. The parent view must call loadInsights() (global, defined by this component) when the Insights tab is activated.
Data source: GET /videos/{video}/insights (JSON) + drill-down routes /insights/country/{code}, /insights/day/{date}, /insights/downloader/{userId}.

View file Placement Notes
resources/views/videos/partials/description-box.blade.php Inside .vdb-wrap, after About panel Used by all three video type views (generic, match, music)
resources/views/videos/show.blade.php Inside .vdb-wrap, after About panel Legacy view (not rendered by controller — kept in sync)

File: resources/views/components/social-links-editor.blade.php
Props: existing — associative array keyed by platform name (e.g. ['twitter' => 'handle', 'whatsapp' => '97312345678']).
Behaviour: Dynamic add/remove rows; each row has a custom icon dropdown to pick the platform and a text input for the value. Supported platforms: twitter, instagram, facebook, youtube, linkedin, tiktok, whatsapp, website, google_location, social_phone, social_email. Hidden clear inputs ensure removed entries are cleared on save. Must be placed inside a <form>.
DB columns: twitter, instagram, facebook, youtube, linkedin, tiktok, website (legacy), whatsapp, google_location, social_phone, social_email.

View file Placement Notes
resources/views/user/profile.blade.php Social tab of Edit Profile modal $socialExisting array passed from @php block above @section('scripts')

<x-date-picker>

File: resources/views/components/date-picker.blade.php
Stored value: YYYY-MM-DD string in a hidden input (same format as <input type="date">).
Props: name, id, value, label, required, class, style, minYear (default 1900), maxYear (default current year).
Behaviour: Day grid (5 columns, 131), month list (JanuaryDecember), year searchable list (descending). Days auto-constrain on month/year change; invalid selected day resets automatically.

View file Field name Notes
resources/views/user/profile.blade.php birthday Replaces <input type="date">
resources/views/auth/register.blade.php birthday Registration form — mandatory

<x-image-cropper>

File: resources/views/components/image-cropper.blade.php
Props: id (unique, required), width (px, default 300), height (px, default 300), shape (circle|square, default circle), folder (storage subfolder), filename (base name without extension), callback (JS function name called with URL on success), update-url (endpoint to POST {path} after crop to update DB), title (modal heading).
Behaviour: Renders a full-screen dark-themed modal with Cropme.js. Shows camera icon on avatar/banner hover (owner only). After crop: POSTs base64 to /image-upload, optionally POSTs the path to update-url, then calls callback(url). Uses local assets (public/js/cropme.min.js, public/css/cropme.min.css). No jQuery required.
Assets needed: public/js/cropme.min.js, public/css/cropme.min.css.
Routes needed: image.upload (POST /image-upload).

View file id / use Notes
resources/views/user/channel.blade.php avatar — circle 300×300 Owner only; update-url = profile.updateAvatar; callback onAvatarSaved
resources/views/user/channel.blade.php banner — square 500×160 Owner only; update-url = profile.updateBanner; callback onBannerSaved
resources/views/layouts/partials/upload-modal.blade.php thumb_upload — square 448×252 Form mode; target-input=thumbnail-modal; output 1280px
resources/views/layouts/partials/edit-video-modal.blade.php thumb_edit — square 448×252 Form mode; target-input=edit-t1-thumbnail-input; output 1280px
resources/views/videos/create.blade.php thumb_create_mobile — square 448×252 Mobile; target-input=thumbnail; output 1280px
resources/views/videos/edit.blade.php thumb_edit_mobile — square 448×252 Mobile; target-input=edit-thumbnail; output 1280px
resources/views/playlists/index.blade.php thumb_pl_create — square 448×252 Form mode; target-input=playlist-thumbnail-input; output 1280px
resources/views/playlists/show.blade.php thumb_pl_edit — square 448×252 Form mode; target-input=playlistThumbnailInput; output 1280px

<x-gender-select>

File: resources/views/components/gender-select.blade.php Props: name, id, value (ISO string: "male" or "female"), label, required, class, style. Behaviour: Custom dropdown with blue ♂ (male) and pink ♀ (female) symbols. No search needed. Stores value as "male" or "female".

View file Field name Notes
resources/views/auth/register.blade.php gender Registration form — mandatory

<x-phone-code-select>

Stored value format: "+973|BH" (dial_code + pipe + ISO2).
To read only the dial code from a stored value: explode('|', $value)[0].

View file Field name Notes
resources/views/user/profile.blade.php phone_code Paired with phone_number text input

<x-country-select>

Stored value: ISO2 code (e.g. "BH").

View file Field name Notes
resources/views/user/profile.blade.php nationality Edit Profile form
resources/views/auth/register.blade.php nationality Registration form — mandatory

<x-timezone-select>

Stored value: IANA timezone string (e.g. "Asia/Bahrain").

View file Field name Notes
resources/views/user/profile.blade.php timezone Edit Profile form

<x-track-editor-form>

File: resources/views/components/track-editor-form.blade.php
Props: prefix (default 't1'), isPrimary (bool, default false), languageName, languageId, titleName, titleId, descName, descId, videoFileInputId.
Behaviour: Renders the full track editor form panel shown inside the Track Editor popup. Contains: optional Primary Track banner (when :is-primary="true"), language dropdown (<x-language-select>), title input, description textarea, video+thumbnail zone (hidden, shown for video/match type via _editApplyMode), and audio+slides zone (hidden, shown for music type). All element IDs are prefixed with edit-{prefix}-*. JS functions editHandleThumbnail(input, prefix), editRemoveThumbnail(event, prefix), editSlidesZoneClick(event, tid), editHandleSlides(files, tid), editClearSlides(event, tid) all accept the prefix/tid param.

View file Prefix used Notes
resources/views/layouts/partials/edit-video-modal.blade.php t1 Primary track only; secondary tracks are built via JS (_editAddExistingTrack)

<x-language-select>

File: resources/views/components/language-select.blade.php
Data source: app/Data/Languages.phpLanguages::forLanguage()
Stored value: ISO 639-1 code (e.g. "ar", "en", "fr").
Props: name, id, value, label, placeholder, required, class, style.
Icon: 2-letter uppercase ISO code rendered as a monospace badge (.lsd-code) — no flag emoji.
Arabic and English are always pinned to the top of the list; all other languages are alphabetical by English name.

Rule: This component must be used for every language picker in the application. Never build a custom <select> or inline list for language selection.

View file Field name Notes
resources/views/layouts/partials/upload-modal.blade.php primary_language Inside accordion track 1 body (id="lang-tracks-section-modal"); extra track language rows use LANG_OPTIONS_MODAL JS constant for inline dynamic CSD
resources/views/videos/create.blade.php primary_language Inside accordion track 1 body (id="lang-tracks-section-create"); extra track language rows use LANG_OPTIONS_CREATE JS constant for inline dynamic CSD
resources/views/components/track-editor-form.blade.php $languageName (prop) Rendered inside the track editor form; used for primary track in edit-video-modal (prefix t1)
resources/views/videos/edit.blade.php primary_language Inside @else (audio only) block; pre-populated with value="{{ $video->language ?? '' }}"

Usage example

{{-- Phone code + number side by side --}}
<div style="display:flex; gap:8px;">
    <x-phone-code-select
        name="phone_code"
        value="+973|BH"
        label="Phone"
        required
        style="width:140px; flex-shrink:0;"
    />
    <input type="tel" name="phone_number" class="form-control">
</div>

{{-- Country / nationality --}}
<x-country-select
    name="nationality"
    label="Nationality"
    placeholder="Select nationality"
    value="{{ old('nationality', $user->nationality) }}"
    required
/>

{{-- Timezone --}}
<x-timezone-select
    name="timezone"
    label="Timezone"
    value="{{ old('timezone', $user->timezone ?? 'Asia/Bahrain') }}"
    required
/>

{{-- Language --}}
<x-language-select
    name="language"
    label="Language"
    placeholder="Select language"
    value="{{ old('language', $video->language ?? '') }}"
    required
/>

Modification checklist

When you modify any of these components, work through this list:

  • Update app/Data/Countries.php if the data structure changes
  • Update all three .blade.php component files if shared CSS/JS changes
  • Update the for*() method in Countries.php that feeds the changed component
  • Re-test every page listed in the usage tables above
  • Add/remove rows from the usage tables if views were added or removed