Compare commits
13 Commits
072d6a7e73
...
2d9eb8640b
Author | SHA1 | Date | |
---|---|---|---|
2d9eb8640b | |||
78bb0f72d5 | |||
a14e1b20c0 | |||
6241539cb3 | |||
b87104c22d | |||
5b8a3b3714 | |||
5f9d23e24a | |||
962f8eb6ad | |||
28a7b3a387 | |||
5dbfc74c3b | |||
7f1dbd0f14 | |||
76790b4ebf | |||
33d9e5ec06 |
@ -34,6 +34,12 @@ REDIS_HOST=redis
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
SCOUT_DRIVER=meilisearch
|
||||
SCOUT_PREFIX=
|
||||
SCOUT_QUEUE=false
|
||||
MEILISEARCH_HOST=http://localhost:7700
|
||||
MEILISEARCH_KEY=
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailhog
|
||||
MAIL_PORT=1125
|
||||
@ -58,8 +64,17 @@ MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
INTEGRITY_HASH_HUMANS_TXT=""
|
||||
INTEGRITY_HASH_ROBOTS_TXT=""
|
||||
INTEGRITY_HASH_COPYRIGHT_HTML=""
|
||||
INTEGRITY_HASH_COPYRIGHT_MD=""
|
||||
INTEGRITY_HASH_RSS_FEED=""
|
||||
INTEGRITY_HASH_ATOM_FEED=""
|
||||
INTEGRITY_HASH_FAVICON_ICO=""
|
||||
INTEGRITY_HASH_FAVICON_PNG=""
|
||||
INTEGRITY_HASH_FAVICON_SVG=""
|
||||
INTEGRITY_HASH_NUNITO_REGULAR_WOFF2_FONT=""
|
||||
INTEGRITY_HASH_POIRETONE_REGULAR_WOFF2_FONT=""
|
||||
INTEGRITY_HASH_WEBMANIFEST_JSON=""
|
||||
INTEGRITY_HASH_MIX_MANIFEST_JSON=""
|
||||
INTEGRITY_HASH_APP_CSS=""
|
||||
INTEGRITY_HASH_APP_JS=""
|
||||
|
@ -9,10 +9,14 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Inertia\Response;
|
||||
use Jenssegers\Agent\Agent;
|
||||
use Laravel\Fortify\Features;
|
||||
use Laravel\Jetstream\Http\Controllers\Inertia\Concerns\ConfirmsTwoFactorAuthentication;
|
||||
use Laravel\Jetstream\Jetstream;
|
||||
|
||||
class UserProfileController extends Controller
|
||||
{
|
||||
use ConfirmsTwoFactorAuthentication;
|
||||
|
||||
/**
|
||||
* Show the general profile settings screen.
|
||||
*
|
||||
@ -27,6 +31,7 @@ class UserProfileController extends Controller
|
||||
$this->validateTwoFactorAuthenticationState($request);
|
||||
|
||||
return Jetstream::inertia()->render($request, 'Profile/Show', [
|
||||
'confirmsTwoFactorAuthentication' => Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm'),
|
||||
'sessions' => $this->sessions($request)->all(),
|
||||
'timezones' => timezone_identifiers_list(),
|
||||
]);
|
||||
|
@ -19,8 +19,8 @@ class CheckCustomSessionData
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ((! session()->has('timezone_name') || empty(session('timezone_name'))) && $request->user()) {
|
||||
session()->put('timezone_name', $request->user()->timezone_name);
|
||||
if ((! session()->has('thing') || empty(session('thing'))) && $request->user()) {
|
||||
session()->put('thing', $request->user()->thing);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
33
src/app/Listeners/SuccessfulLogin.php
Normal file
33
src/app/Listeners/SuccessfulLogin.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Illuminate\Auth\Events\Login;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class SuccessfulLogin
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \Illuminate\Auth\Events\Login $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Login $event): void
|
||||
{
|
||||
Session::put('timezone_name', $event->user->timezone_name);
|
||||
}
|
||||
}
|
32
src/app/Listeners/SuccessfulLogout.php
Normal file
32
src/app/Listeners/SuccessfulLogout.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Illuminate\Auth\Events\Logout;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class SuccessfulLogout
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \Illuminate\Auth\Events\Logout $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Logout $event)
|
||||
{
|
||||
Session::forget('timezone_name');
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@ class User extends Authenticatable
|
||||
/** @var array */
|
||||
protected $appends = [
|
||||
'full_name',
|
||||
'name_full',
|
||||
'surname_full',
|
||||
'profile_photo_url',
|
||||
];
|
||||
|
||||
@ -142,7 +142,7 @@ class User extends Authenticatable
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Casts\Attribute
|
||||
*/
|
||||
public function fullNameReversed(): Attribute
|
||||
public function surnameFull(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value, $attributes) => "{$attributes['surname']}, {$attributes['name']}",
|
||||
|
76
src/app/Providers/CarbonServiceProvider.php
Normal file
76
src/app/Providers/CarbonServiceProvider.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class CarbonServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Carbon::macro('getHumanInterval', function ($other) {
|
||||
$isNow = $other === null;
|
||||
|
||||
if ($isNow) {
|
||||
$other = static::now($this->tz);
|
||||
}
|
||||
|
||||
$diffInMinutes = $this->diffInMinutes($other);
|
||||
|
||||
$minutes = $diffInMinutes % 60;
|
||||
|
||||
$diffInHours = $diffInMinutes / 60;
|
||||
$hours = $diffInHours % 60;
|
||||
|
||||
$diffInDays = $diffInHours / 24;
|
||||
$days = $diffInDays % 24;
|
||||
|
||||
$diffInWeeks = $diffInDays / 24;
|
||||
$weeks = $diffInWeeks % 24;
|
||||
|
||||
$diffInMonths = $diffInWeeks / 7;
|
||||
$months = $diffInMonths % 7;
|
||||
|
||||
$diffInYears = $diffInMonths / 12;
|
||||
$years = $diffInYears % 12;
|
||||
|
||||
$outputArray = [];
|
||||
if ($years > 0) {
|
||||
$outputArray['years'] = "$years years";
|
||||
}
|
||||
if ($months > 0) {
|
||||
$outputArray['months'] = "$months months";
|
||||
}
|
||||
if ($weeks > 0 && $days === 0) {
|
||||
$outputArray['weeks'] = "$weeks weeks";
|
||||
}
|
||||
if ($days > 0) {
|
||||
$outputArray['days'] = "$days days";
|
||||
}
|
||||
if ($hours > 0) {
|
||||
$outputArray['hours'] = "$hours hours";
|
||||
}
|
||||
if ($minutes > 0) {
|
||||
$outputArray['minutes'] = "$minutes minutes";
|
||||
}
|
||||
|
||||
return implode(', ', $outputArray);
|
||||
});
|
||||
}
|
||||
}
|
52
src/app/Providers/EventServiceProvider.php
Normal file
52
src/app/Providers/EventServiceProvider.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Listeners\SuccessfulLogin;
|
||||
//use App\Listeners\SuccessfulLogout;
|
||||
use Illuminate\Auth\Events\Login;
|
||||
//use Illuminate\Auth\Events\Logout;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event to listener mappings for the application.
|
||||
*
|
||||
* @var array<class-string, array<int, class-string>>
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
Login::class => [
|
||||
SuccessfulLogin::class,
|
||||
],
|
||||
/*Logout::class => [
|
||||
SuccessfulLogout::class,
|
||||
],*/
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if events and listeners should be automatically discovered.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldDiscoverEvents()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
22
src/config/app.php
Normal file
22
src/config/app.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
...
|
||||
|
||||
return [
|
||||
|
||||
...
|
||||
|
||||
'providers' => [
|
||||
|
||||
...
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
...
|
||||
App\Providers\CarbonServiceProvider::class,
|
||||
],
|
||||
|
||||
...
|
||||
|
||||
];
|
@ -5,6 +5,8 @@ if (! function_exists('snake2Title')) {
|
||||
* Convert a snake case string to a title with spaces
|
||||
* and every word capitalized.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $stakeSlug A snake case string, commonly a slug
|
||||
*
|
||||
* @return string
|
||||
@ -22,6 +24,8 @@ if (! function_exists('carbon')) {
|
||||
* It will attempt to find a timezone in the current
|
||||
* session but default to UTC.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string|null $timestring
|
||||
*
|
||||
* @return \Carbon\Carbon
|
||||
@ -40,6 +44,8 @@ if (! function_exists('jddayofweek')) {
|
||||
/**
|
||||
* Returns the day of the week. Can return a string or an integer depending on the mode.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int|null $intDay
|
||||
* @param int $mode
|
||||
*
|
||||
@ -59,10 +65,106 @@ if (! function_exists('jddayofweek')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('is_serialized')) {
|
||||
/**
|
||||
* Check a value to find if it was serialized.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param bool $strict
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function is_serialized($data, bool $strict = true): bool
|
||||
{
|
||||
// If it isn't a string, it isn't serialized.
|
||||
if (! is_string($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = trim($data);
|
||||
if ('N;' === $data) {
|
||||
return true;
|
||||
}
|
||||
if (strlen($data) < 4) {
|
||||
return false;
|
||||
}
|
||||
if (':' !== $data[1]) {
|
||||
return false;
|
||||
}
|
||||
if ($strict) {
|
||||
$lastc = substr($data, -1);
|
||||
if (';' !== $lastc && '}' !== $lastc) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$semicolon = strpos($data, ';');
|
||||
$brace = strpos($data, '}');
|
||||
// Either ; or } must exist.
|
||||
if (!$semicolon && !$brace) {
|
||||
return false;
|
||||
}
|
||||
// But neither must be in the first X characters.
|
||||
if ($semicolon && $semicolon < 3) {
|
||||
return false;
|
||||
}
|
||||
if ($brace && $brace < 4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$token = $data[0];
|
||||
switch ($token) {
|
||||
case 's':
|
||||
if ($strict) {
|
||||
if ('"' !== substr($data, -2, 1)) {
|
||||
return false;
|
||||
}
|
||||
} elseif (!strpos($data, '"')) {
|
||||
return false;
|
||||
}
|
||||
// Or else fall through.
|
||||
case 'a':
|
||||
case 'O':
|
||||
return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
|
||||
case 'b':
|
||||
case 'i':
|
||||
case 'd':
|
||||
$end = $strict ? '$' : '';
|
||||
return (bool) preg_match("/^{$token}:[0-9.E+-]+;$end/", $data);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('maybe_unserialize')) {
|
||||
/**
|
||||
* Unserialize data only if it was serialized. Will return
|
||||
* an array if it was a serialized string, otherwise it
|
||||
* will return whatever was passed into the function
|
||||
* leaving it untouched.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function maybe_unserialize($data)
|
||||
{
|
||||
if (is_serialized($data)) { // Don't attempt to unserialize data that wasn't serialized going in.
|
||||
return @unserialize(trim($data));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('cel2Fah')) {
|
||||
/**
|
||||
* Convert from celsius to fahrenheit.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $celsius
|
||||
* @param int $precision
|
||||
*
|
||||
@ -78,6 +180,8 @@ if (! function_exists('fah2Cel')) {
|
||||
/**
|
||||
* Convert from fahrenheit to celsius.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $fahrenheit
|
||||
* @param int $precision
|
||||
*
|
||||
@ -93,6 +197,8 @@ if (! function_exists('meters2Miles')) {
|
||||
/**
|
||||
* Convert from meters to miles.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $meters
|
||||
* @param int $precision
|
||||
*
|
||||
@ -108,6 +214,8 @@ if (! function_exists('kilometers2Miles')) {
|
||||
/**
|
||||
* Convert from kilometers to meters.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $kilometers
|
||||
* @param int $precision
|
||||
*
|
||||
@ -123,6 +231,8 @@ if (! function_exists('m2Km')) {
|
||||
/**
|
||||
* Convert from meters to kilometers.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $meters
|
||||
* @param int $precision
|
||||
*
|
||||
@ -138,6 +248,8 @@ if (! function_exists('mm2Inches')) {
|
||||
/**
|
||||
* Convert from milimeters to inches.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $milimeters
|
||||
* @param int $precision
|
||||
*
|
||||
@ -153,6 +265,8 @@ if (! function_exists('pa2Mbar')) {
|
||||
/**
|
||||
* Convert from pascals to milibars.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param float|int|string $pascals
|
||||
* @param int $precision
|
||||
*
|
||||
|
BIN
src/public/fonts/RobotoMono/RobotoMono-Bold.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-Bold.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/RobotoMono/RobotoMono-BoldItalic.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-BoldItalic.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/public/fonts/RobotoMono/RobotoMono-Italic.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-Italic.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/RobotoMono/RobotoMono-Regular.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-Regular.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/RobotoMono/RobotoMono-SemiBold.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-SemiBold.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/RobotoMono/RobotoMono-SemiBoldItalic.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-SemiBoldItalic.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/RobotoMono/RobotoMono-VariableFont_wght.woff2
Normal file
BIN
src/public/fonts/RobotoMono/RobotoMono-VariableFont_wght.woff2
Normal file
Binary file not shown.
@ -1,3 +1,11 @@
|
||||
.spin {
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/** https://thinkdobecreate.com/articles/css-animating-newly-added-element/ **/
|
||||
|
||||
.anim-show {
|
||||
|
@ -3,7 +3,7 @@
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
@import 'animations.css';
|
||||
@import 'typography.css';
|
||||
@import 'fontfaces.css';
|
||||
|
||||
@import 'components/buttons.css';
|
||||
@import 'components/cards.css';
|
||||
|
@ -2,6 +2,58 @@
|
||||
/** | Sans fonts | **/
|
||||
/** +--------------------------------+ **/
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: url('/fonts/OpenSans/OpenSans-Regular.woff2') format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: url('/fonts/OpenSans/OpenSans-Italic.woff2') format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: url('/fonts/OpenSans/OpenSans-SemiBold.woff2') format("woff2");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: url('/fonts/OpenSans/OpenSans-SemiBoldItalic.woff2') format("woff2");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: url('/fonts/OpenSans/OpenSans-Bold.woff2') format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: url('/fonts/OpenSans/OpenSans-BoldItalic.woff2') format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/** +--------------------------------+ **/
|
||||
/** | Serif fonts | **/
|
||||
/** +--------------------------------+ **/
|
||||
|
||||
@font-face {
|
||||
font-family: "Nunito";
|
||||
src: url('/fonts/Nunito/Nunito-Regular.woff2') format("woff2");
|
||||
@ -50,12 +102,6 @@
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/** +--------------------------------+ **/
|
||||
/** | Serif fonts | **/
|
||||
/** +--------------------------------+ **/
|
||||
|
||||
/**/
|
||||
|
||||
/** +--------------------------------+ **/
|
||||
/** | Monospace fonts | **/
|
||||
/** +--------------------------------+ **/
|
||||
|
81
src/resources/js/Components/DarkModeToggle.vue
Normal file
81
src/resources/js/Components/DarkModeToggle.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<script setup>
|
||||
import { reactive, computed, onBeforeMount, provide } from 'vue'
|
||||
|
||||
const emit = defineEmits(['themeUpdate'])
|
||||
|
||||
let settings = reactive({
|
||||
theme: 'light',
|
||||
})
|
||||
|
||||
const htmlNode = document.documentElement
|
||||
let mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
// computed properties
|
||||
const isDarkMode = computed(() => {
|
||||
return settings.theme === 'dark'
|
||||
})
|
||||
|
||||
const isLightMode = computed(() => {
|
||||
return settings.theme === 'light'
|
||||
})
|
||||
|
||||
// lifecycle hooks
|
||||
onBeforeMount(() => {
|
||||
window.addEventListener('storage', update)
|
||||
|
||||
if (mediaQuery?.addEventListener) {
|
||||
mediaQuery.addEventListener('change', update)
|
||||
}
|
||||
|
||||
update()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
provide('darkMode', isDarkMode)
|
||||
})
|
||||
|
||||
// methods
|
||||
const update = () => {
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
setModeDark()
|
||||
} else {
|
||||
setModeLight()
|
||||
}
|
||||
|
||||
emit('themeUpdate')
|
||||
}
|
||||
|
||||
const setModeDark = () => {
|
||||
settings.theme = 'dark'
|
||||
localStorage.theme = 'dark'
|
||||
htmlNode.classList.remove('light')
|
||||
htmlNode.classList.add('dark')
|
||||
}
|
||||
|
||||
const setModeLight = () => {
|
||||
settings.theme = 'light'
|
||||
localStorage.theme = 'light'
|
||||
htmlNode.classList.remove('dark')
|
||||
htmlNode.classList.add('light')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center cursor-pointer">
|
||||
<svg v-show="isDarkMode" @click="setModeLight" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
|
||||
<svg v-show="isLightMode" @click="setModeDark" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
@ -1,5 +1,62 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { Link } from '@inertiajs/inertia-vue3'
|
||||
|
||||
const props = defineProps({
|
||||
paginationData: Object,
|
||||
})
|
||||
|
||||
// computed properties
|
||||
const onFirstPage = computed(() => {
|
||||
return props.paginationData.current_page === 1
|
||||
})
|
||||
|
||||
const hasMorePages = computed(() => {
|
||||
return props.paginationData.current_page < props.paginationData.last_page
|
||||
})
|
||||
|
||||
const nextPageUrl = computed(() => {
|
||||
return props.paginationData.next_page_url
|
||||
})
|
||||
|
||||
const previousPageUrl = computed(() => {
|
||||
return props.paginationData.prev_page_url
|
||||
})
|
||||
|
||||
const firstItem = computed(() => {
|
||||
if (props.paginationData.from == null) {
|
||||
return '-'
|
||||
}
|
||||
return props.paginationData.from
|
||||
})
|
||||
|
||||
const lastItem = computed(() => {
|
||||
if (props.paginationData.to == null) {
|
||||
return '-'
|
||||
}
|
||||
return props.paginationData.to
|
||||
})
|
||||
|
||||
const total = computed(() => {
|
||||
if (isNaN(props.paginationData.total)) {
|
||||
return '0'
|
||||
}
|
||||
return props.paginationData.total
|
||||
})
|
||||
|
||||
// watchers
|
||||
|
||||
// lifecycle hooks
|
||||
|
||||
// methods
|
||||
const isFirstOrLastOrDots = (index, linksLength, label) => {
|
||||
return index === 0 || index === linksLength - 1 || label.includes('...')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="pagi !== undefined" class="flex items-center justify-between" role="navigation">
|
||||
<nav v-if="paginationData !== undefined" class="flex items-center justify-between" role="navigation">
|
||||
<div class="flex justify-between flex-1 sm:hidden">
|
||||
<span v-if="onFirstPage" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-gray-100 border border-gray-300 cursor-default leading-5 rounded-md">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
@ -45,8 +102,8 @@
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
<div v-for="(link, index) in pagi.links">
|
||||
<Link v-if="!isFirstOrLastOrDots(index, pagi.links.length, link.label)" :class="{ 'bg-blue-200' : link.active }" :href="link.url" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" v-html="link.label"></Link>
|
||||
<div v-for="(link, index) in paginationData.links">
|
||||
<Link v-if="!isFirstOrLastOrDots(index, paginationData.links.length, link.label)" :class="{ 'bg-blue-200' : link.active }" :href="link.url" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" v-html="link.label"></Link>
|
||||
<span v-else-if="link.label === '...'" aria-disabled="true" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5" v-html="link.label"></span>
|
||||
</div>
|
||||
|
||||
@ -65,63 +122,3 @@
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue"
|
||||
import { Link } from "@inertiajs/inertia-vue3"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
pagi: Object,
|
||||
},
|
||||
|
||||
components: {
|
||||
Link,
|
||||
},
|
||||
|
||||
computed: {
|
||||
onFirstPage() {
|
||||
return this.pagi.current_page === 1
|
||||
},
|
||||
|
||||
hasMorePages() {
|
||||
return this.pagi.current_page < this.pagi.last_page
|
||||
},
|
||||
|
||||
nextPageUrl() {
|
||||
return this.pagi.next_page_url
|
||||
},
|
||||
|
||||
previousPageUrl() {
|
||||
return this.pagi.prev_page_url
|
||||
},
|
||||
|
||||
firstItem() {
|
||||
if (this.pagi.from == null) {
|
||||
return '-'
|
||||
}
|
||||
return this.pagi.from
|
||||
},
|
||||
|
||||
lastItem() {
|
||||
if (this.pagi.to == null) {
|
||||
return '-'
|
||||
}
|
||||
return this.pagi.to
|
||||
},
|
||||
|
||||
total() {
|
||||
if (isNaN(this.pagi.total)) {
|
||||
return '0'
|
||||
}
|
||||
return this.pagi.total
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
isFirstOrLastOrDots(index, linksLength, label) {
|
||||
return index === 0 || index === linksLength - 1 || label.includes('...')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
@ -48,7 +48,7 @@
|
||||
{{-- <link href="https://de.example.com/2010/06/title-of-my-article" rel="alternate" hreflang="de"> --}}
|
||||
|
||||
<!-- Android web manifest file -->
|
||||
{{-- <link href="{{ url('/.webmanifest') }}" rel="manifest"> --}}
|
||||
{{-- <link href="{{ url('/site.webmanifest') }}" rel="manifest" integrity="{{ env('INTEGRITY_HASH_WEBMANIFEST_JSON') }}"> --}}
|
||||
|
||||
<!-- Files listing who was involved in this site and copyrights -->
|
||||
<link href="{{ url('/humans.txt') }}" rel="author" integrity="{{ env('INTEGRITY_HASH_HUMANS_TXT') }}">
|
||||
@ -63,9 +63,9 @@
|
||||
<link rel="alternate" type="text/xml+oembed" href="https://example.com/services/oembed?url=http%3A%2F%2Fexample.com%2Ffoo%2F&format=xml" title="oEmbed Profile: XML">
|
||||
|
||||
<!-- Favicon -->
|
||||
{{-- <link href="{{ asset('/favicon.ico') }}" rel="icon" sizes="16x16" type="image/icon"> --}}
|
||||
{{-- <link href="{{ asset('/favicon.svg') }}" rel="icon" type="image/svg+xml"> --}}
|
||||
{{-- <link href="{{ asset('/favicon.png') }}" rel="icon" sizes="192x192"> --}}
|
||||
{{-- <link href="{{ asset('/favicon.ico') }}" rel="icon" sizes="16x16" type="image/icon" integrity="{{ env('INTEGRITY_HASH_FAVICON_ICO') }}"> --}}
|
||||
{{-- <link href="{{ asset('/favicon.svg') }}" rel="icon" type="image/svg+xml" integrity="{{ env('INTEGRITY_HASH_FAVICON_SVG') }}"> --}}
|
||||
{{-- <link href="{{ asset('/favicon.png') }}" rel="icon" sizes="192x192" integrity="{{ env('INTEGRITY_HASH_FAVICON_PNG') }}"> --}}
|
||||
|
||||
<!-- Font preloads (should be done for each font file) -->
|
||||
<link href="{{ asset('/fonts/Nunito/Nunito-Regular.woff2') }}" rel="preload" as="font" type="font/woff2" integrity="{{ env('INTEGRITY_HASH_NUNITO_REGULAR_WOFF2_FONT') }}" crossorigin="anonymous">
|
||||
|
@ -3,9 +3,18 @@ const { exec } = require('child_process');
|
||||
|
||||
const resIntegrityFiles = [
|
||||
{ "envKey": "HUMANS_TXT", "path": "public/humans.txt" },
|
||||
{ "envKey": "ROBOTS_TXT", "path": "public/robots.txt" },
|
||||
{ "envKey": "COPYRIGHT_HTML", "path": "public/copyright.html" },
|
||||
{ "envKey": "COPYRIGHT_MD", "path": "public/copyright.md" },
|
||||
{ "envKey": "RSS_FEED", "path": "public/rss.xml" },
|
||||
{ "envKey": "ATOM_FEED", "path": "public/feed.atom" },
|
||||
{ "envKey": "FAVICON_ICO", "path": "public/favicon.ico" },
|
||||
{ "envKey": "FAVICON_SVG", "path": "public/favicon.svg" },
|
||||
{ "envKey": "FAVICON_PNG", "path": "public/favicon.png" },
|
||||
{ "envKey": "NUNITO_REGULAR_WOFF2_FONT", "path": "public/fonts/Nunito/Nunito-Regular.woff2" },
|
||||
{ "envKey": "POIRETONE_REGULAR_WOFF2_FONT", "path": "public/fonts/PoiretOne/PoiretOne-Regular.woff2" },
|
||||
{ "envKey": "WEBMANIFEST_JSON", "path": "public/site.webmanifest" },
|
||||
{ "envKey": "MIX_MANIFEST_JSON", "path": "public/mix-manifest.json" },
|
||||
{ "envKey": "APP_CSS", "path": "public/css/app.css" },
|
||||
{ "envKey": "APP_JS", "path": "public/js/app.js" },
|
||||
];
|
||||
|
Loading…
x
Reference in New Issue
Block a user