Compare commits
48 Commits
e78324e92a
...
master
Author | SHA1 | Date | |
---|---|---|---|
ac751f5f28
|
|||
a1aecdf3e2
|
|||
a747ef0129
|
|||
80aa487e7e
|
|||
5048abc3e6
|
|||
afd34e9b4e
|
|||
a81ce8a922
|
|||
9d25befeba
|
|||
5d9f7e3997
|
|||
ecfae3e01a
|
|||
9e6c93ba95
|
|||
231dd959af
|
|||
5607425bde
|
|||
ac99d29109
|
|||
d4f0044656
|
|||
9d455bdcf7
|
|||
3b39ae0470
|
|||
9551f661c1
|
|||
c02b40071f
|
|||
a0dc0e01f3
|
|||
8876f07ac6
|
|||
2ac78ef539
|
|||
56cef79880
|
|||
aa5753cc82
|
|||
4dcb6b6ebe
|
|||
22946bc031
|
|||
e2ce420764
|
|||
7583883d64
|
|||
fb5bde70ef
|
|||
5e2a386b15
|
|||
75d052af88
|
|||
87cd634613
|
|||
48a34d5acf
|
|||
05ca3574b0
|
|||
739c2b79e9
|
|||
6e7ded098b
|
|||
64d4d326b7
|
|||
fdba9f1ae7
|
|||
27032f433a
|
|||
0c8a6c410d
|
|||
3f340d57fc
|
|||
995cc32578
|
|||
5f5b443df7
|
|||
e5366171fd
|
|||
1c650fbb64
|
|||
a784d44d16
|
|||
e27aa8969f
|
|||
433ad39a08
|
60
colors.md
Normal file
60
colors.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
Colors
|
||||||
|
|
||||||
|
### Sunbaked Mint
|
||||||
|
HEX: #80e8d4
|
||||||
|
RGB: 128, 232, 212
|
||||||
|
CMYK: 43, 0, 25, 0
|
||||||
|
|
||||||
|
|
||||||
|
### Honey Dijon
|
||||||
|
HEX: #e2ae61
|
||||||
|
RGB: 226, 174, 97
|
||||||
|
CMYK: 1, 32, 72, 0
|
||||||
|
|
||||||
|
|
||||||
|
### Ultimate Gray
|
||||||
|
HEX: #97999b
|
||||||
|
RGB: 151, 153, 155
|
||||||
|
CMYK (approximation): 44, 35, 34, 1
|
||||||
|
|
||||||
|
|
||||||
|
### Frosty Blue
|
||||||
|
HEX: #bcddfc
|
||||||
|
RGB: 188, 221, 252
|
||||||
|
CMYK: 23, 5, 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
### Electric Tangerine
|
||||||
|
HEX: #ff825c
|
||||||
|
RGB: 255, 130, 92
|
||||||
|
CMYK: 0, 61, 64, 0
|
||||||
|
|
||||||
|
|
||||||
|
### Holo Lilac
|
||||||
|
HEX: #c5d2fe
|
||||||
|
RGB: 197, 210, 254
|
||||||
|
CMYK: 22, 17, 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
### Poppy Sunset
|
||||||
|
HEX: #ee645b
|
||||||
|
RGB: 238, 100, 91
|
||||||
|
CMYK: 2, 76, 62, 0
|
||||||
|
|
||||||
|
|
||||||
|
### Lime Nouveau
|
||||||
|
HEX: #cddf8b
|
||||||
|
RGB: 205, 223, 139
|
||||||
|
CMYK: 22, 1, 58, 5
|
||||||
|
|
||||||
|
|
||||||
|
### Very Peri
|
||||||
|
HEX: #6667ab
|
||||||
|
RGB: 102, 103, 171
|
||||||
|
CMYK (approximation): 40, 40, 0, 33
|
||||||
|
|
||||||
|
|
||||||
|
### Classic Blue
|
||||||
|
HEX: #0f4c81
|
||||||
|
RGB: 15, 76, 129
|
||||||
|
CMYK (approximation): 99, 76, 24, 8
|
@ -27,6 +27,6 @@ modified="git diff --diff-filter=M --name-only --cached | grep \".php$\""
|
|||||||
ignore="resources/lang,resources/views,bootstrap/helpers,database/migrations,bin"
|
ignore="resources/lang,resources/views,bootstrap/helpers,database/migrations,bin"
|
||||||
phpcs="vendor/bin/phpcs --report=code --colors --report-width=80 --ignore=${ignore}"
|
phpcs="vendor/bin/phpcs --report=code --colors --report-width=80 --ignore=${ignore}"
|
||||||
|
|
||||||
__run "1/1" "code sniffer" "${modified} | xargs -r ${phpcs}"
|
__run "1/2" "php lint" "${modified} | xargs -r php -l"
|
||||||
# __run "2/3" "php lint" "${modified} | xargs -r php -l"
|
__run "2/2" "code sniffer" "${modified} | xargs -r ${phpcs}"
|
||||||
# __run "3/3" "phpstan" "${modified} | xargs -r vendor/bin/phpstan analyse"
|
# __run "3/3" "phpstan" "${modified} | xargs -r vendor/bin/phpstan analyse"
|
||||||
|
@ -4,7 +4,7 @@ APP_KEY=base64:hSCTwZ507IdKQ5QJHJ+mQw0DSMgDdAspasjwHCdiB8Y=
|
|||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_DOMAIN=localhost
|
APP_DOMAIN=localhost
|
||||||
APP_URL="https://${APP_DOMAIN}"
|
APP_URL="https://${APP_DOMAIN}"
|
||||||
APP_UID_BYTES=8
|
ADMIN_EMAIL=""
|
||||||
|
|
||||||
GIT_HASH="00000000"
|
GIT_HASH="00000000"
|
||||||
GIT_TAG="x.x.x"
|
GIT_TAG="x.x.x"
|
||||||
@ -28,6 +28,9 @@ FILESYSTEM_DRIVER=local
|
|||||||
QUEUE_CONNECTION=sync
|
QUEUE_CONNECTION=sync
|
||||||
SESSION_DRIVER=database
|
SESSION_DRIVER=database
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
|
#SESSION_STORE=redis
|
||||||
|
#SESSION_DOMAIN="${APP_DOMAIN}"
|
||||||
|
#SESSION_SECURE_COOKIE=true
|
||||||
|
|
||||||
MEMCACHED_HOST=memcache
|
MEMCACHED_HOST=memcache
|
||||||
|
|
||||||
@ -35,6 +38,15 @@ REDIS_HOST=redis
|
|||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=mailpit
|
||||||
|
MAIL_PORT=1025
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@${APP_DOMAIN}"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
SCOUT_DRIVER=meilisearch
|
SCOUT_DRIVER=meilisearch
|
||||||
SCOUT_PREFIX=
|
SCOUT_PREFIX=
|
||||||
SCOUT_QUEUE=false
|
SCOUT_QUEUE=false
|
||||||
@ -43,14 +55,15 @@ MEILISEARCH_KEY=
|
|||||||
MEILISEARCH_PRIVATE_KEY=
|
MEILISEARCH_PRIVATE_KEY=
|
||||||
MEILISEARCH_PUBLIC_KEY=
|
MEILISEARCH_PUBLIC_KEY=
|
||||||
|
|
||||||
MAIL_MAILER=smtp
|
GOOGLE_GEOCODE_API_KEY=
|
||||||
MAIL_HOST=mailhog
|
|
||||||
MAIL_PORT=1125
|
MINIO_USERNAME=
|
||||||
MAIL_USERNAME=null
|
MINIO_PASSWORD=
|
||||||
MAIL_PASSWORD=null
|
MINIO_DEFAULT_REGION=us-west-1
|
||||||
MAIL_ENCRYPTION=null
|
MINIO_BUCKET=
|
||||||
MAIL_FROM_ADDRESS="no-reply@${APP_DOMAIN}"
|
MINIO_URL=
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MINIO_ENDPOINT=
|
||||||
|
MINIO_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=
|
AWS_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_SECRET_ACCESS_KEY=
|
||||||
@ -61,8 +74,14 @@ AWS_USE_PATH_STYLE_ENDPOINT=false
|
|||||||
PUSHER_APP_ID=
|
PUSHER_APP_ID=
|
||||||
PUSHER_APP_KEY=
|
PUSHER_APP_KEY=
|
||||||
PUSHER_APP_SECRET=
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_HOST=
|
||||||
|
PUSHER_PORT=443
|
||||||
|
PUSHER_SCHEME=https
|
||||||
PUSHER_APP_CLUSTER=mt1
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
VITE_SSL_KEY_FILE_PATH="/code/docker/configs/nginx/ssls/${APP_DOMAIN}/${APP_DOMAIN}.key"
|
||||||
|
VITE_SSL_CERT_FILE_PATH="/code/docker/configs/nginx/ssls/${APP_DOMAIN}/${APP_DOMAIN}.crt"
|
||||||
|
|
||||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
MIX_PUSHER_HOST="${PUSHER_HOST}"
|
MIX_PUSHER_HOST="${PUSHER_HOST}"
|
||||||
@ -84,3 +103,13 @@ INTEGRITY_HASH_WEBMANIFEST_JSON=""
|
|||||||
INTEGRITY_HASH_MIX_MANIFEST_JSON=""
|
INTEGRITY_HASH_MIX_MANIFEST_JSON=""
|
||||||
INTEGRITY_HASH_APP_CSS=""
|
INTEGRITY_HASH_APP_CSS=""
|
||||||
INTEGRITY_HASH_APP_JS=""
|
INTEGRITY_HASH_APP_JS=""
|
||||||
|
|
||||||
|
## Clockwork debug helpers
|
||||||
|
## default values are set, except for the main enable switch
|
||||||
|
CLOCKWORK_ENABLE=true
|
||||||
|
CLOCKWORK_WEB=true
|
||||||
|
CLOCKWORK_AUTHENTICATION=false
|
||||||
|
#CLOCKWORK_AUTHENTICATION_PASSWORD=VerySecretPassword
|
||||||
|
CLOCKWORK_CACHE_COLLECT_VALUES=true
|
||||||
|
#CLOCKWORK_DATABASE_SLOW_THRESHOLD= # time in miliseconds
|
||||||
|
#CLOCKWORK_REQUESTS_SLOW_THRESHOLD= # time in miliseconds
|
||||||
|
@ -1,17 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PSR2" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/src/Standards/PSR2/ruleset.xml">
|
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PSR2" xsi:noNamespaceSchemaLocation="vendor/slevomat/coding-standard">
|
||||||
<description>PHP Codesniffer ruleset for this project</description>
|
<description>PHP Codesniffer ruleset for this project</description>
|
||||||
|
|
||||||
<file>./app/Models</file>
|
|
||||||
<file>./config</file>
|
|
||||||
<file>./database</file>
|
|
||||||
<file>./helpers</file>
|
|
||||||
<file>./lang</file>
|
|
||||||
<file>./resources/views</file>
|
|
||||||
<file>./routes</file>
|
|
||||||
|
|
||||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
|
||||||
|
|
||||||
<arg name="basepath" value="."/>
|
<arg name="basepath" value="."/>
|
||||||
<arg name="parallel" value="4"/>
|
<arg name="parallel" value="4"/>
|
||||||
<arg name="report" value="full"/>
|
<arg name="report" value="full"/>
|
||||||
@ -24,6 +14,16 @@
|
|||||||
|
|
||||||
<autoload>./vendor/autoload.php</autoload>
|
<autoload>./vendor/autoload.php</autoload>
|
||||||
|
|
||||||
|
<file>./app/Models</file>
|
||||||
|
<file>./config</file>
|
||||||
|
<file>./database</file>
|
||||||
|
<file>./helpers</file>
|
||||||
|
<file>./lang</file>
|
||||||
|
<file>./resources/views</file>
|
||||||
|
<file>./routes</file>
|
||||||
|
|
||||||
|
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||||
|
|
||||||
<!-- Don't hide tokenizer exceptions -->
|
<!-- Don't hide tokenizer exceptions -->
|
||||||
<rule ref="Internal.Tokenizer.Exception">
|
<rule ref="Internal.Tokenizer.Exception">
|
||||||
<type>error</type>
|
<type>error</type>
|
||||||
|
6
src/.well-known/security.txt
Normal file
6
src/.well-known/security.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Contact: mailto:person@domain.com
|
||||||
|
Contact: https://twitter.com/person
|
||||||
|
Expires: 2024-09-14T14:00:00.000Z
|
||||||
|
Encryption: https://keybase.io/person
|
||||||
|
Encryption: https://domain.com/pgp-key.txt
|
||||||
|
Preferred-Languages: en
|
30
src/SECURITY.md
Normal file
30
src/SECURITY.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!-- AUTO-GENERATED, DO NOT EDIT! -->
|
||||||
|
<!-- Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/SECURITY.md -->
|
||||||
|
|
||||||
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
|
|
||||||
|
- [Security Policy](#security-policy)
|
||||||
|
- [Supported Versions](#supported-versions)
|
||||||
|
- [Reporting a Vulnerability](#reporting-a-vulnerability)
|
||||||
|
|
||||||
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
We release patches for security vulnerabilities. Which versions are eligible for
|
||||||
|
receiving such patches depends on the CVSS v3.0 Rating:
|
||||||
|
|
||||||
|
| CVSS v3.0 | Supported Versions |
|
||||||
|
| --------- | ----------------------------------------- |
|
||||||
|
| 9.0-10.0 | Releases within the previous three months |
|
||||||
|
| 4.0-8.9 | Most recent release |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Please report (suspected) security vulnerabilities to
|
||||||
|
**[security@ory.sh](mailto:security@ory.sh)**. You will receive a response from
|
||||||
|
us within 48 hours. If the issue is confirmed, we will release a patch as soon
|
||||||
|
as possible depending on complexity but historically within a few days.
|
28
src/app/Actions/Chained/GenerateProfilePhoto.php
Normal file
28
src/app/Actions/Chained/GenerateProfilePhoto.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Chained;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class GenerateProfilePhoto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Generates a profile photo for a user and saves it
|
||||||
|
* to disk.
|
||||||
|
*
|
||||||
|
* @package App\Actions\Chained\GenerateProfilePhoto
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param \App\Models\User $user
|
||||||
|
* @param \Closure $next
|
||||||
|
*
|
||||||
|
* @return \App\Models\User
|
||||||
|
*/
|
||||||
|
public function __invoke(User $user, Closure $next): User
|
||||||
|
{
|
||||||
|
//
|
||||||
|
|
||||||
|
return $next($user);
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ class CreateUser extends Command
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->info("$email created successfully.");
|
$this->info("$email created successfully.");
|
||||||
return 0;
|
return Command::SUCCESS;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->error('Unable to create your user.');
|
$this->error('Unable to create your user.');
|
||||||
$this->line($e->getMessage());
|
$this->line($e->getMessage());
|
||||||
|
@ -52,7 +52,7 @@ class ResetPassword extends Command
|
|||||||
$column = 'email';
|
$column = 'email';
|
||||||
$value = $email;
|
$value = $email;
|
||||||
} else {
|
} else {
|
||||||
$column = strtolower($this->choice('What column would you like to search by?', ['ID', 'Email']));
|
$column = strtolower($this->choice('What database column would you like to search by?', ['ID', 'Email']));
|
||||||
$value = $this->ask("Please provide an $column to search for");
|
$value = $this->ask("Please provide an $column to search for");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class ResetPassword extends Command
|
|||||||
try {
|
try {
|
||||||
$user->update(['password' => Hash::make($password)]);
|
$user->update(['password' => Hash::make($password)]);
|
||||||
$this->info("User {$user->id} ({$user->email}) password update successful!");
|
$this->info("User {$user->id} ({$user->email}) password update successful!");
|
||||||
return 0;
|
return Command::SUCCESS;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->error('Unable to set the password!');
|
$this->error('Unable to set the password!');
|
||||||
$this->line($e->getMessage());
|
$this->line($e->getMessage());
|
||||||
|
83
src/app/Console/Commands/TranslationCheckerCommand.php
Normal file
83
src/app/Console/Commands/TranslationCheckerCommand.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class TranslationCheckerCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'i18n:check-json {--l|locale= : The source of truth locale}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Uses one JSON file as the source of truth to check other lang files against.';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$masterLocale = 'en';
|
||||||
|
if (!empty($this->option('locale'))) {
|
||||||
|
$masterLocale = trim($this->option('locale'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$langDir = base_path('lang');
|
||||||
|
$langFiles = scandir($langDir);
|
||||||
|
$bigCount = count($langFiles);
|
||||||
|
for ($i = 0; $i < $bigCount; $i++) {
|
||||||
|
if (! preg_match('/.*\.json$/', $langFiles[$i]) || "{$masterLocale}.json" === $langFiles[$i]) {
|
||||||
|
unset($langFiles[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$langFilesCount = count($langFiles);
|
||||||
|
$this->info("Checking {$langFilesCount} file(s) against {$masterLocale}...");
|
||||||
|
|
||||||
|
$masterLocaleTranslations = translations(base_path("lang/{$masterLocale}.json"));
|
||||||
|
|
||||||
|
foreach ($langFiles as $localeFile) {
|
||||||
|
$otherLocaleTranslations = translations(base_path("lang/{$localeFile}"));
|
||||||
|
$counts = 0;
|
||||||
|
$mergedTranslations = $this->recursiveMergeArray($masterLocaleTranslations, $otherLocaleTranslations, $counts);
|
||||||
|
file_put_contents(base_path("lang/{$localeFile}"), json_encode($mergedTranslations, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT));
|
||||||
|
$this->info("{$localeFile} had {$counts} missing translations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recursiveMergeArray(array $firstArray, array $secondArray, int &$counts)
|
||||||
|
{
|
||||||
|
$mergedArray = $secondArray;
|
||||||
|
foreach ($firstArray as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
if (!array_key_exists($key, $secondArray)) {
|
||||||
|
$mergedArray[$key] = $value;
|
||||||
|
} else {
|
||||||
|
$subDiff = $this->recursiveMergeArray($value, $secondArray[$key], $counts);
|
||||||
|
if (!empty($subDiff)) {
|
||||||
|
$mergedArray[$key] = $subDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!array_key_exists($key, $secondArray)) {
|
||||||
|
$counts++;
|
||||||
|
$mergedArray[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mergedArray;
|
||||||
|
}
|
||||||
|
}
|
@ -2,21 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
use Jenssegers\Agent\Agent;
|
|
||||||
use Laravel\Fortify\Features;
|
use Laravel\Fortify\Features;
|
||||||
use Laravel\Jetstream\Http\Controllers\Inertia\Concerns\ConfirmsTwoFactorAuthentication;
|
use Laravel\Jetstream\Http\Controllers\Inertia\UserProfileController as JetstreamUserProfileController;
|
||||||
use Laravel\Jetstream\Jetstream;
|
use Laravel\Jetstream\Jetstream;
|
||||||
|
|
||||||
class UserProfileController extends Controller
|
class UserProfileController extends JetstreamUserProfileController
|
||||||
{
|
{
|
||||||
use ConfirmsTwoFactorAuthentication;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the general profile settings screen.
|
* Show the general profile settings screen.
|
||||||
*
|
*
|
||||||
@ -36,56 +29,4 @@ class UserProfileController extends Controller
|
|||||||
'timezones' => timezone_identifiers_list(),
|
'timezones' => timezone_identifiers_list(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current sessions.
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Support\Collection
|
|
||||||
*/
|
|
||||||
public function sessions(Request $request): Collection
|
|
||||||
{
|
|
||||||
if (config('session.driver') !== 'database') {
|
|
||||||
return collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
return collect(
|
|
||||||
DB::connection(config('session.connection'))->table(config('session.table', 'sessions'))
|
|
||||||
->where('user_id', $request->user()->getAuthIdentifier())
|
|
||||||
->orderBy('last_activity', 'desc')
|
|
||||||
->get()
|
|
||||||
)->map(function ($session) use ($request) {
|
|
||||||
$agent = $this->createAgent($session);
|
|
||||||
|
|
||||||
return (object) [
|
|
||||||
'agent' => [
|
|
||||||
'is_desktop' => $agent->isDesktop(),
|
|
||||||
'platform' => $agent->platform(),
|
|
||||||
'browser' => $agent->browser(),
|
|
||||||
],
|
|
||||||
'ip_address' => $session->ip_address,
|
|
||||||
'is_current_device' => $session->id === $request->session()->getId(),
|
|
||||||
'last_active' => Carbon::createFromTimestamp($session->last_activity)->diffForHumans(),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new agent instance from the given session.
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*
|
|
||||||
* @param mixed $session
|
|
||||||
*
|
|
||||||
* @return \Jenssegers\Agent\Agent
|
|
||||||
*/
|
|
||||||
protected function createAgent($session): Agent
|
|
||||||
{
|
|
||||||
return tap(new Agent, function ($agent) use ($session) {
|
|
||||||
$agent->setUserAgent($session->user_agent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
33
src/app/Http/Kernel.php
Normal file
33
src/app/Http/Kernel.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
|
class Kernel extends HttpKernel
|
||||||
|
{
|
||||||
|
//...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware groups.
|
||||||
|
*
|
||||||
|
* @var array<string, array<int, class-string|string>>
|
||||||
|
*/
|
||||||
|
protected $middlewareGroups = [
|
||||||
|
'web' => [
|
||||||
|
//...
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
\App\Http\Middleware\SetLocale::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
//...
|
||||||
|
],
|
||||||
|
|
||||||
|
'admin' => [
|
||||||
|
//...
|
||||||
|
\Laravel\Jetstream\Http\Middleware\ShareInertiaData::class,
|
||||||
|
\App\Http\Middleware\SetLocale::class,
|
||||||
|
\App\Http\Middleware\AuthorizeAdmin::class,
|
||||||
|
//...
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
@ -5,22 +5,20 @@ namespace App\Http\Middleware;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class CheckCustomSessionData
|
class AuthorizeAdmin
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Closure $next
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next)
|
||||||
{
|
{
|
||||||
if ((! session()->has('thing') || empty(session('thing'))) && $request->user()) {
|
if ($request->user()->email !== env('ADMIN_EMAIL')) {
|
||||||
session()->put('thing', $request->user()->thing);
|
abort(HTTP_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\Language;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Inertia\Middleware;
|
use Inertia\Middleware;
|
||||||
|
|
||||||
@ -41,19 +42,58 @@ class HandleInertiaRequests extends Middleware
|
|||||||
*/
|
*/
|
||||||
public function share(Request $request): array
|
public function share(Request $request): array
|
||||||
{
|
{
|
||||||
|
$localeFields = ['locale_name', 'iso_code', 'name', 'localized_name'];
|
||||||
|
$currentLocale = $request->session()->get('locale', null);
|
||||||
|
if (is_null($currentLocale)) {
|
||||||
|
$currentLocale = Language::where(['locale_name' => 'en', 'iso_code' => 'en_US'])->get($localeFields)[0]->toArray();
|
||||||
|
$request->session()->put('locale', [
|
||||||
|
'locale_name' => $currentLocale['locale_name'],
|
||||||
|
'iso_code' => $currentLocale['iso_code'],
|
||||||
|
'name' => $currentLocale['name'],
|
||||||
|
'localized_name' => $currentLocale['localized_name'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$localeFilePath = base_path("lang/{$currentLocale['locale_name']}.json");
|
||||||
|
|
||||||
$notifications = [];
|
$notifications = [];
|
||||||
|
$notificationsCount = count($notifications);
|
||||||
$unreadNotifications = false;
|
$unreadNotifications = false;
|
||||||
if (! is_null($request->user())) {
|
if (! is_null($request->user())) {
|
||||||
$notifications = $request->user()->notifications;
|
$notifications = $request->user()->notifications;
|
||||||
|
$notificationsCount = count($notifications);
|
||||||
|
for ($i = 0; $i < $notificationsCount; $i++) {
|
||||||
|
$newData = $notifications[$i]->data;
|
||||||
|
$createdAt = carbon($notifications[$i]->created_at);
|
||||||
|
$dateFormat = 'F j';
|
||||||
|
if (!$createdAt->is(gmdate('Y'))) {
|
||||||
|
$dateFormat = 'F j, Y';
|
||||||
|
}
|
||||||
|
$newData['created_at_date'] = $createdAt->copy()->format($dateFormat);
|
||||||
|
$newData['created_at_time'] = $createdAt->copy()->format('H:i');
|
||||||
|
$notifications[$i]->data = $newData;
|
||||||
|
}
|
||||||
if (count($request->user()->unreadNotifications) > 0) {
|
if (count($request->user()->unreadNotifications) > 0) {
|
||||||
$unreadNotifications = true;
|
$unreadNotifications = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_merge(parent::share($request), [
|
$additionalData = [
|
||||||
'appName' => config('app.name'),
|
'appName' => config('app.name'),
|
||||||
|
|
||||||
|
'availableLocales' => Language::get($localeFields),
|
||||||
|
'currentLocale' => $currentLocale,
|
||||||
|
'language' => translations($localeFilePath),
|
||||||
|
|
||||||
'notifications' => $notifications,
|
'notifications' => $notifications,
|
||||||
'unreadNotifications' => $unreadNotifications,
|
'unreadNotifications' => $unreadNotifications,
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if (! is_null($request->user())) {
|
||||||
|
if ($request->user()->email === env('ADMIN_EMAIL')) {
|
||||||
|
$additionalData['is_admin_user'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge(parent::share($request), $additionalData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
src/app/Http/Middleware/SetLocale.php
Normal file
29
src/app/Http/Middleware/SetLocale.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class SetLocale
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @package App\Http\Middleware\SetLocale
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
if (session()->has('locale')) {
|
||||||
|
app()->setLocale(session('locale')['locale']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
51
src/app/Models/Casts/MoneyCastable.php
Normal file
51
src/app/Models/Casts/MoneyCastable.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Casts;
|
||||||
|
|
||||||
|
use Brick\Money\Money;
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class MoneyCastable implements CastsAttributes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the value from the database and modify it to casted type
|
||||||
|
* before returning.
|
||||||
|
*
|
||||||
|
* @package App\Models\Casts\MoneyCastable
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model The Model that is being used
|
||||||
|
* @param string $key The attribute key
|
||||||
|
* @param mixed $value The value stored in the database
|
||||||
|
* @param array $attributes The array of model attributes
|
||||||
|
*
|
||||||
|
* @return \Brick\Money\Money
|
||||||
|
*/
|
||||||
|
public function get(Model $model, string $key, $value, array $attributes): Money
|
||||||
|
{
|
||||||
|
return Money::ofMinor($attributes['amount'], $attributes['currency']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the data .
|
||||||
|
*
|
||||||
|
* @package App\Models\Casts\MoneyCastable
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model The Model that is being used
|
||||||
|
* @param string $key The attribute key
|
||||||
|
* @param mixed $value The value stored in the database
|
||||||
|
* @param array $attributes The array of model attributes
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set(Model $model, string $key, $value, array $attributes)
|
||||||
|
{
|
||||||
|
if (! $value instanceof Money) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value->getMinorAmount()->toInt();
|
||||||
|
}
|
||||||
|
}
|
@ -7,18 +7,20 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Prunable;
|
use Illuminate\Database\Eloquent\Prunable;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class Language extends Model
|
class Language extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use Prunable;
|
use Prunable;
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $table = 'languages';
|
protected $table = 'languages';
|
||||||
|
|
||||||
/** @var array<int,string> */
|
/** @var array<int,string> */
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'locale',
|
'locale_name',
|
||||||
'iso_code',
|
'iso_code',
|
||||||
'name',
|
'name',
|
||||||
'localized_name',
|
'localized_name',
|
||||||
|
23
src/app/Models/Scopes/OnTeamScope.php
Normal file
23
src/app/Models/Scopes/OnTeamScope.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Scopes;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Scope;
|
||||||
|
|
||||||
|
class OnTeamScope implements Scope
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Apply the scope to a given Eloquent query builder.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $builder
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function apply(Builder $builder, Model $model): void
|
||||||
|
{
|
||||||
|
$builder->where('current_team_id', );
|
||||||
|
}
|
||||||
|
}
|
@ -2,19 +2,24 @@
|
|||||||
|
|
||||||
namespace App\Models\Traits;
|
namespace App\Models\Traits;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
|
||||||
trait FormattedPhoneTrait
|
trait FormattedPhoneTrait
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Format a phone number to be human readable.
|
* Format a phone number to be human readable.
|
||||||
*
|
*
|
||||||
|
* @package Namespace\App\Contact
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*
|
*
|
||||||
* @return string
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function getPhoneNumberAttribute(): string
|
protected function phoneNumber(): Attribute
|
||||||
{
|
{
|
||||||
$phoneLength = strlen($this->phone);
|
return Attribute::make(
|
||||||
$phoneNumber = preg_replace('//', '', $this->phone);
|
get: function (mixed $value, array $attributes) {
|
||||||
|
$phoneLength = strlen($attributes['phone_number']);
|
||||||
|
$phoneNumber = preg_replace('/[^0-9]/', '', $attributes['phone_number']);
|
||||||
|
|
||||||
if ($phoneLength > 10) {
|
if ($phoneLength > 10) {
|
||||||
$countryCode = substr($phoneNumber, 0, $phoneLength - 10);
|
$countryCode = substr($phoneNumber, 0, $phoneLength - 10);
|
||||||
@ -37,19 +42,10 @@ trait FormattedPhoneTrait
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $phoneNumber;
|
return $phoneNumber;
|
||||||
}
|
},
|
||||||
|
set: function (mixed $value) {
|
||||||
/**
|
return preg_replace('/[^0-9]/', '', $value);
|
||||||
* Remove all non-numeric characters from the phone number.
|
},
|
||||||
*
|
);
|
||||||
* @since 1.0.0
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setPhoneNumberAttribute($value): void
|
|
||||||
{
|
|
||||||
$this->attributes['phone'] = preg_replace('/[^0-9]/', '', $value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
88
src/app/Models/Traits/HasProfilePhoto.php
Normal file
88
src/app/Models/Traits/HasProfilePhoto.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Laravel\Jetstream;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
trait HasProfilePhoto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Update the user's profile photo.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\UploadedFile $photo
|
||||||
|
* @param string $storagePath
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function updateProfilePhoto(UploadedFile $photo, $storagePath = 'profile-photos')
|
||||||
|
{
|
||||||
|
tap($this->profile_photo_path, function ($previous) use ($photo, $storagePath) {
|
||||||
|
$this->forceFill([
|
||||||
|
'profile_photo_path' => $photo->storePublicly(
|
||||||
|
$storagePath, ['disk' => $this->profilePhotoDisk()]
|
||||||
|
),
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
if ($previous) {
|
||||||
|
Storage::disk($this->profilePhotoDisk())->delete($previous);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the user's profile photo.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteProfilePhoto()
|
||||||
|
{
|
||||||
|
if (! Features::managesProfilePhotos()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->profile_photo_path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::disk($this->profilePhotoDisk())->delete($this->profile_photo_path);
|
||||||
|
|
||||||
|
$this->forceFill([
|
||||||
|
'profile_photo_path' => null,
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URL to the user's profile photo.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Casts\Attribute
|
||||||
|
*/
|
||||||
|
public function profilePhotoUrl(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::get(function () {
|
||||||
|
return $this->profile_photo_path
|
||||||
|
? Storage::disk($this->profilePhotoDisk())->url($this->profile_photo_path)
|
||||||
|
: $this->defaultProfilePhotoUrl();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default profile photo URL if no profile photo has been uploaded.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function defaultProfilePhotoUrl()
|
||||||
|
{
|
||||||
|
return 'https://avatars.test/avatar?size=256';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the disk that profile photos should be stored on.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function profilePhotoDisk()
|
||||||
|
{
|
||||||
|
return isset($_ENV['VAPOR_ARTIFACT_NAME']) ? 's3' : config('jetstream.profile_photo_disk', 'public');
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ class User extends Authenticatable
|
|||||||
'name',
|
'name',
|
||||||
'surname',
|
'surname',
|
||||||
'timezone_name',
|
'timezone_name',
|
||||||
|
'language_id',
|
||||||
'current_team_id',
|
'current_team_id',
|
||||||
'profile_photo_path',
|
'profile_photo_path',
|
||||||
'email',
|
'email',
|
||||||
@ -183,4 +184,17 @@ class User extends Authenticatable
|
|||||||
{
|
{
|
||||||
return $this->morphOne(Address::class, 'addressable');
|
return $this->morphOne(Address::class, 'addressable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language relationship.
|
||||||
|
*
|
||||||
|
* @package App\Models\User
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function language(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Language::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
"spatie/laravel-permission": "",
|
"spatie/laravel-permission": "",
|
||||||
"zizaco/entrust": "",
|
"zizaco/entrust": "",
|
||||||
|
|
||||||
|
// Security stuff
|
||||||
|
"abuseipdb/laravel": "", // API to check an IP for abusive behavior with option to report IP for abusive behavior
|
||||||
|
|
||||||
// Options/Flags
|
// Options/Flags
|
||||||
"spatie/laravel-model-flags": "", // add a simple true/false flag to any model
|
"spatie/laravel-model-flags": "", // add a simple true/false flag to any model
|
||||||
|
|
||||||
@ -30,6 +33,9 @@
|
|||||||
"torann/geoip": "", // support for multiple GeoIP services
|
"torann/geoip": "", // support for multiple GeoIP services
|
||||||
"coderjerk/nasa-php": "", // NASA API integrations
|
"coderjerk/nasa-php": "", // NASA API integrations
|
||||||
|
|
||||||
|
// File management
|
||||||
|
"czim/laravel-paperclip": "", // attach files to an Eloquent model
|
||||||
|
|
||||||
// Media (image/video/audio) Management
|
// Media (image/video/audio) Management
|
||||||
"intervention/image": "", // image manipulation
|
"intervention/image": "", // image manipulation
|
||||||
"intervention/imagecache": "", // caching for intervention/image
|
"intervention/imagecache": "", // caching for intervention/image
|
||||||
@ -37,6 +43,7 @@
|
|||||||
"spatie/laravel-image-optimizer": "", // optimize png, jpg, svg, and gif
|
"spatie/laravel-image-optimizer": "", // optimize png, jpg, svg, and gif
|
||||||
"unisharp/laravel-filemanager": "", // File manager (including uploads)
|
"unisharp/laravel-filemanager": "", // File manager (including uploads)
|
||||||
"pbmedia/laravel-ffmpeg": "", // Integration with FFMpeg
|
"pbmedia/laravel-ffmpeg": "", // Integration with FFMpeg
|
||||||
|
"maestroerror/php-heic-to-jpg": "", // Use Go(?) to convert HEIC/HEIF to JPEG
|
||||||
|
|
||||||
// Localization
|
// Localization
|
||||||
"mcamara/laravel-localization": "", // localization that also handles route translations
|
"mcamara/laravel-localization": "", // localization that also handles route translations
|
||||||
@ -59,6 +66,7 @@
|
|||||||
"spatie/laravel-sitemap": "", // generate site maps
|
"spatie/laravel-sitemap": "", // generate site maps
|
||||||
"artesaos/seotools": "", // automatic SEO tools
|
"artesaos/seotools": "", // automatic SEO tools
|
||||||
"eumanito/php-capitalize-names": "", // Capitalize names with funky rules
|
"eumanito/php-capitalize-names": "", // Capitalize names with funky rules
|
||||||
|
"robinvdvleuten/ulid": "", // Universally Unique Lexicographically Sortable Identifier (ULID)
|
||||||
|
|
||||||
// Others/uncategorized
|
// Others/uncategorized
|
||||||
"appstract/laravel-opcache": "", // provides artisan commands to interact with opcache (not sure if site-specific)
|
"appstract/laravel-opcache": "", // provides artisan commands to interact with opcache (not sure if site-specific)
|
||||||
@ -68,6 +76,7 @@
|
|||||||
"protonemedia/laravel-verify-new-email": "", // must verify new email address before email update will be completed
|
"protonemedia/laravel-verify-new-email": "", // must verify new email address before email update will be completed
|
||||||
"protonemedia/inertiajs-tables-laravel-query-builder": "", // datatables for InertiaJS/Vue and Laravel
|
"protonemedia/inertiajs-tables-laravel-query-builder": "", // datatables for InertiaJS/Vue and Laravel
|
||||||
|
|
||||||
|
"ikechukwukalu/requirepin": "", // use password/pin protection for routes
|
||||||
|
|
||||||
"doctrine/dbal": "", // useful for artisan db:show
|
"doctrine/dbal": "", // useful for artisan db:show
|
||||||
},
|
},
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
namespace Database\Factories;
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Language;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Laravel\Jetstream\Features;
|
use Laravel\Jetstream\Features;
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ class UserFactory extends Factory
|
|||||||
'email' => $this->faker->unique()->safeEmail(),
|
'email' => $this->faker->unique()->safeEmail(),
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'timezone_name' => $this->faker->timezone(),
|
'timezone_name' => $this->faker->timezone(),
|
||||||
|
'language_id' => Language::all()->random()->id,
|
||||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||||
'remember_token' => Str::random(10),
|
'remember_token' => Str::random(10),
|
||||||
];
|
];
|
||||||
@ -69,4 +72,25 @@ class UserFactory extends Factory
|
|||||||
'ownedTeams'
|
'ownedTeams'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the user should have an avatar image.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withAvatar()
|
||||||
|
{
|
||||||
|
if (! Features::enabled(Features::profilePhotos())) {
|
||||||
|
return $this->state([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$imageUrl = 'https://avatars.test/';
|
||||||
|
$imageContents = file_get_contents($imageUrl);
|
||||||
|
$imageName = md5($imageContents) . '.jpg';
|
||||||
|
Storage::put("public/avatars/{$imageName}", $imageContents);
|
||||||
|
|
||||||
|
return $this->state([
|
||||||
|
'profile_photo_path' => "avatars/{$imageName}"
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ return new class extends Migration
|
|||||||
{
|
{
|
||||||
Schema::create('languages', function (Blueprint $table) {
|
Schema::create('languages', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('locale')->index();
|
$table->string('locale_name')->index();
|
||||||
$table->string('iso_code')->unique();
|
$table->string('iso_code')->unique();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('localized_name');
|
$table->string('localized_name');
|
||||||
|
@ -16,49 +16,18 @@ class LanguageSeeder extends Seeder
|
|||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
$languages = [
|
$languagesData = [
|
||||||
[
|
['locale' => 'en', 'iso_code' => 'en_US', 'name' => 'English', 'localized_name' => 'English (US)'],
|
||||||
'iso_code' => 'en_US',
|
['locale' => 'de', 'iso_code' => 'de_DE', 'name' => 'German', 'localized_name' => 'Deutsch'],
|
||||||
'locale' => 'en',
|
['locale' => 'fr', 'iso_code' => 'fr_FR', 'name' => 'French', 'localized_name' => 'Français'],
|
||||||
'title' => 'English',
|
['locale' => 'jp', 'iso_code' => 'jp_JP', 'name' => 'Japanese', 'localized_name' => '日本'],
|
||||||
'title_localized' => 'English',
|
['locale' => 'mx', 'iso_code' => 'es_MX', 'name' => 'Spanish', 'localized_name' => 'Español'],
|
||||||
],
|
|
||||||
[
|
|
||||||
'iso_code' => 'de_DE',
|
|
||||||
'locale' => 'de',
|
|
||||||
'title' => 'German',
|
|
||||||
'title_localized' => 'Deutsch',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'iso_code' => 'fr_FR',
|
|
||||||
'locale' => 'fr',
|
|
||||||
'title' => 'French',
|
|
||||||
'title_localized' => 'Français',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'iso_code' => 'es_SP',
|
|
||||||
'locale' => 'es',
|
|
||||||
'title' => 'Spanish',
|
|
||||||
'title_localized' => 'Español',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'iso_code' => 'jp_JP',
|
|
||||||
'locale' => 'jp',
|
|
||||||
'title' => 'Japanese',
|
|
||||||
'title_localized' => '日本',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'iso_code' => 'zh_TW',
|
|
||||||
'locale' => 'zh',
|
|
||||||
'title' => 'Taiwanese',
|
|
||||||
'title_localized' => '台湾',
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$startTime = Carbon::now();
|
$startTime = Carbon::now();
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
|
||||||
foreach ($languages as $language) {
|
foreach ($languagesData as $language) {
|
||||||
$datetime = $startTime->copy()->addMinute($offset)->toDateTimeString();
|
$datetime = $startTime->copy()->addMinute($offset)->toDateTimeString();
|
||||||
$offset++;
|
$offset++;
|
||||||
$language['created_at'] = $datetime;
|
$language['created_at'] = $datetime;
|
||||||
|
@ -24,6 +24,7 @@ $httpCodes = [
|
|||||||
204 => 'HTTP_NO_CONTENT',
|
204 => 'HTTP_NO_CONTENT',
|
||||||
205 => 'HTTP_RESET_CONTENT',
|
205 => 'HTTP_RESET_CONTENT',
|
||||||
206 => 'HTTP_PARTIAL_CONTENT',
|
206 => 'HTTP_PARTIAL_CONTENT',
|
||||||
|
218 => 'HTTP_THIS_IS_FINE', // Apache specific
|
||||||
|
|
||||||
// 300's (redirections / "go away")
|
// 300's (redirections / "go away")
|
||||||
300 => 'HTTP_MULTIPLE_CHOICE',
|
300 => 'HTTP_MULTIPLE_CHOICE',
|
||||||
@ -57,12 +58,14 @@ $httpCodes = [
|
|||||||
236 => 'HTTP_UPGRADE_REQUIRED',
|
236 => 'HTTP_UPGRADE_REQUIRED',
|
||||||
428 => 'HTTP_PRECONDITION_REQUIRED',
|
428 => 'HTTP_PRECONDITION_REQUIRED',
|
||||||
429 => 'HTTP_TOO_MANY_REQUESTS',
|
429 => 'HTTP_TOO_MANY_REQUESTS',
|
||||||
|
444 => 'HTTP_NO_RESPONSE', // Nginx specific, used internally to return no info and close connection immediately
|
||||||
451 => 'HTTP_GAG_ORDER',
|
451 => 'HTTP_GAG_ORDER',
|
||||||
|
|
||||||
// 500's (server-level problem, process died or configuration is incorrect / "server screwed up")
|
// 500's (server-level problem, process died or configuration is incorrect / "server screwed up")
|
||||||
500 => 'HTTP_SERVER_ERROR',
|
500 => 'HTTP_SERVER_ERROR',
|
||||||
501 => 'HTTP_NOT_IMPLEMENTED',
|
501 => 'HTTP_NOT_IMPLEMENTED',
|
||||||
503 => 'HTTP_UNAVAILABLE',
|
503 => 'HTTP_UNAVAILABLE',
|
||||||
|
529 => 'HTTP_SITE_OVERLOADED', // Qualys specific, API response to indicate server resources are over capacity
|
||||||
530 => 'HTTP_SUSPENDED',
|
530 => 'HTTP_SUSPENDED',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// phpcs:ignore
|
||||||
/**
|
/**
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Global String Functions
|
| Global String Functions
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This is a home for functions that don't belong to any one class and
|
| This is a home for functions that don't belong to any one class and
|
||||||
| that should be available anywhere in the application.
|
| that should be available anywhere in the application.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (! function_exists('snake2Title')) {
|
if (! function_exists('snake2Title')) {
|
||||||
/**
|
/**
|
||||||
* Convert a snake case string to a title with spaces
|
* Convert a snake case string to a title with spaces
|
||||||
* and every word capitalized.
|
* and every word capitalized.
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @param string $snakeSlug A snake case string, commonly a slug
|
||||||
*
|
*
|
||||||
* @param string $stakeSlug A snake case string, commonly a slug
|
* @since 1.0.0
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -15,6 +15,47 @@ require_once "functions/temperatures.php";
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (! function_exists('clamp')) {
|
||||||
|
/**
|
||||||
|
* Ensure a numerical value is between two bounds.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*
|
||||||
|
* @param int|float|string $number The value to be clamped between two other values.
|
||||||
|
* @param int|float|string $minNumber The miminum value for clamping bounds.
|
||||||
|
* @param int|float|string $maxNumber The maximum value for clamping bounds.
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*
|
||||||
|
* @return int|float
|
||||||
|
*/
|
||||||
|
function clamp($number, $minNumber, $maxNumber)
|
||||||
|
{
|
||||||
|
if (! is_numeric($number)) {
|
||||||
|
throw new Exception('Clamp number must be numeric in value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! is_numeric($minNumber)) {
|
||||||
|
throw new Exception('Clamped minimum number must be numeric in value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! is_numeric($maxNumber)) {
|
||||||
|
throw new Exception('Clamped maximum number must be numeric in value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$returnValue = $number;
|
||||||
|
if ($minNumber >= $number) {
|
||||||
|
$returnValue = $minNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($maxNumber <= $number) {
|
||||||
|
$returnValue = $maxNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (! function_exists('humanBytes')) {
|
if (! function_exists('humanBytes')) {
|
||||||
/**
|
/**
|
||||||
* Convert bytes to a human-friendly format.
|
* Convert bytes to a human-friendly format.
|
||||||
@ -195,7 +236,7 @@ if (! function_exists('maybe_unserialize')) {
|
|||||||
*
|
*
|
||||||
* @param mixed $data
|
* @param mixed $data
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return array|string|bool
|
||||||
*/
|
*/
|
||||||
function maybe_unserialize($data)
|
function maybe_unserialize($data)
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,88 @@
|
|||||||
{
|
{
|
||||||
"key": "translation that has :attribute in the string."
|
"titles": {
|
||||||
|
"welcome": "Welcome",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"secure_area": "Secure Area",
|
||||||
|
"forgot_password": "Forgot Password",
|
||||||
|
"my_profile": "My Profile",
|
||||||
|
"edit_profile": "Edit Profile",
|
||||||
|
"log_in": "Log In To Your Account",
|
||||||
|
"register": "Register A New Account",
|
||||||
|
"reset_password": "Reset Password",
|
||||||
|
"two_factor_confirmation": "Two-factor Confirmation",
|
||||||
|
"verify_email": "Email Verification"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"type": "Type",
|
||||||
|
"none": "None",
|
||||||
|
"title": "Title",
|
||||||
|
"name": "Name",
|
||||||
|
"created_at": "Created At",
|
||||||
|
"updated_at": "Updated At",
|
||||||
|
"actions": "Actions",
|
||||||
|
"email": "Email",
|
||||||
|
"timezone_name": "Timezone",
|
||||||
|
"language": "Language",
|
||||||
|
"password": "Password",
|
||||||
|
"confirm_password": "Confirm Password",
|
||||||
|
"current_password": "Current Password",
|
||||||
|
"remember_me": "Remember me",
|
||||||
|
"first_name": "First Name",
|
||||||
|
"last_name": "Surname",
|
||||||
|
"forgot_password": "Forgot your password?",
|
||||||
|
"already_registered": "Already registered?",
|
||||||
|
"code": "Code",
|
||||||
|
"recovery_code": "Recovery Code",
|
||||||
|
"use_recovery_code": "Use a recovery code",
|
||||||
|
"use_authentication_code": "Use an authentication code",
|
||||||
|
"email_verification": "Email Verification",
|
||||||
|
"user": "User",
|
||||||
|
"optional": "optional"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"save": "Save",
|
||||||
|
"update": "Update",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"log_in": "Log in",
|
||||||
|
"log_out": "Log Out",
|
||||||
|
"register": "Register",
|
||||||
|
"edit_profile": "Edit Profile",
|
||||||
|
"confirm_password": "Confirm Password",
|
||||||
|
"reset_password": "Reset Password",
|
||||||
|
"email_password_reset_link": "Email Password Reset Link",
|
||||||
|
"resend_verification_email": "Resend Verification Email"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"manage_team": "Manage Team",
|
||||||
|
"team_settings": "Team Settings",
|
||||||
|
"create_new_team": "Create New Team",
|
||||||
|
"switch_teams": "Switch Teams",
|
||||||
|
"manage_account": "Manage Account",
|
||||||
|
"profile": "My Profile",
|
||||||
|
"admin_settings": "Admin Settings",
|
||||||
|
"api_tokens": "API Tokens"
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"show_from_to_count": "Showing :from to :to of :count results"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"saved": "Saved.",
|
||||||
|
"verified": "Verified!",
|
||||||
|
"unverified": "Unverified.",
|
||||||
|
"new_email_verification_sent": "A new verification link has been sent to the email address you provided in your profile settings."
|
||||||
|
},
|
||||||
|
"pages": {
|
||||||
|
"profile": {
|
||||||
|
"titles": {
|
||||||
|
"update_password": "Update Your Password"
|
||||||
|
},
|
||||||
|
"text_blocks": {
|
||||||
|
"unverified_message": "Please verify your account.",
|
||||||
|
"verified_message": "Thanks for being amazing.",
|
||||||
|
"secure_password": "Ensure your account is using a long, random password to stay secure."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,24 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run development",
|
"dev": "vite",
|
||||||
"development": "mix",
|
"build": "vite build"
|
||||||
"watch": "mix watch",
|
|
||||||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
|
||||||
"hot": "mix watch --hot",
|
|
||||||
"prod": "npm run production",
|
|
||||||
"production": "mix --production"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"laravel-mix": "^6.0.49",
|
"@vitejs/plugin-vue": "^3.0.0",
|
||||||
|
"laravel-vite-plugin": "^0.6.0",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"postcss-import": "^14.1.0",
|
"vite": "^3.0.0"
|
||||||
"vue-loader": "^17.0.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@inertiajs/inertia": "^0.11.0",
|
"@inertiajs/inertia": "^0.11.0",
|
||||||
"@inertiajs/inertia-vue3": "^0.6.0",
|
"@inertiajs/inertia-vue3": "^0.6.0",
|
||||||
"@inertiajs/progress": "^0.2.7",
|
"@inertiajs/progress": "^0.2.7",
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.0",
|
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||||
"@tailwindcss/forms": "^0.5.2",
|
"@tailwindcss/line-clamp": "^0.4.2",
|
||||||
"@tailwindcss/typography": "^0.5.2",
|
"@tailwindcss/typography": "^0.5.2",
|
||||||
"@vueuse/core": "^8.7.5",
|
"@vueuse/core": "^8.7.5",
|
||||||
|
"apexcharts": "^3.35.5",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"daisyui": "^2.29.0",
|
"daisyui": "^2.29.0",
|
||||||
"dayjs": "^1.11.3",
|
"dayjs": "^1.11.3",
|
||||||
|
6
src/postcss.config.js
Normal file
6
src/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Bold.woff2
Normal file
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Bold.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Italic.woff2
Normal file
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Italic.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Medium.woff2
Normal file
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Medium.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Regular.woff2
Normal file
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-Regular.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-SemiBold.woff2
Normal file
BIN
src/public/fonts/BarlowCondensed/BarlowCondensed-SemiBold.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-Bold.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-Bold.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-BoldItalic.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-Italic.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-Italic.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-Medium.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-Medium.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-MediumItalic.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-Regular.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-Regular.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-SemiBold.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-SemiBold.woff2
Normal file
Binary file not shown.
BIN
src/public/fonts/Montserrat/Montserrat-SemiBoldItalic.woff2
Normal file
BIN
src/public/fonts/Montserrat/Montserrat-SemiBoldItalic.woff2
Normal file
Binary file not shown.
59
src/resources/css/animations/bubbleup.css
Normal file
59
src/resources/css/animations/bubbleup.css
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
<div class="bubbles-container">
|
||||||
|
<div class="bubbles">
|
||||||
|
<span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
|
||||||
|
<span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
|
||||||
|
<span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
|
||||||
|
<span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
|
||||||
|
<span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
**/
|
||||||
|
|
||||||
|
.bubbles-container {
|
||||||
|
background: hsl(216, 57.1%, 11%);
|
||||||
|
min-height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubbles {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubbles span {
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 30px;
|
||||||
|
margin: 0 4px;
|
||||||
|
position: relative;
|
||||||
|
width: 30px;
|
||||||
|
/*animation: bubble 15s linear infinite;*/
|
||||||
|
/*animation-name: bubbleup;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-duration: calc(300s / calc(var(--i) * 5));*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubbles span:nth-child(odd) {
|
||||||
|
background: hsl(191, 66.8%, 58.6%);
|
||||||
|
box-shadow: 0 0 0 10px hsla(191, 66.8%, 58.6%, 0.3), 0 0 50px hsl(191, 66.8%, 58.6%), 0 0 100px hsl(191, 66.8%, 58.6%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubbles span:nth-child(even) {
|
||||||
|
background: hsl(339, 100%, 58.8%);
|
||||||
|
box-shadow: 0 0 0 10px hsla(339, 100%, 58.8%, 0.3), 0 0 50px hsl(339, 100%, 58.8%), 0 0 100px hsl(339, 100%, 58.8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-bubble {
|
||||||
|
animation-name: bubbleup;
|
||||||
|
animation-duration: calc(300s / calc(var(--i) * 5));
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bubbleup {
|
||||||
|
0% { transform: translateY(100px) scale(0); }
|
||||||
|
100% { transform: translateY(-100px) scale(1); }
|
||||||
|
}
|
30
src/resources/css/animations/ripple.css
Normal file
30
src/resources/css/animations/ripple.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
:root {
|
||||||
|
--ripple-color: #fff;
|
||||||
|
--ripple-radius: 9999px;
|
||||||
|
--ripple-duration: 600ms;
|
||||||
|
--ripple-timing-function: linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ripple {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ripple span {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: var(--ripple-radius);
|
||||||
|
opacity: 0.5;
|
||||||
|
background: var(--ripple-color);
|
||||||
|
transform: scale(0);
|
||||||
|
animation: ripple var(--ripple-duration) var(--ripple-timing-function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ripple {
|
||||||
|
to {
|
||||||
|
transform: scale(4);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
@ -26,19 +26,60 @@
|
|||||||
@import 'components/navbars.css';
|
@import 'components/navbars.css';
|
||||||
@import 'components/pagination.css';
|
@import 'components/pagination.css';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary-text-color: hsl(0, 90.4%, 59.2%);
|
||||||
|
--primary-text-color-gradient-start: hsl(270, 66.9%, 47.5%);
|
||||||
|
--primary-text-color-gradient-end: hsl(330, 100%, 50%);
|
||||||
|
|
||||||
|
--text-info-color: hsl(191, 82%, 50%);
|
||||||
|
--text-info-color-gradient-start: hsl(227, 100%, 56.5%);
|
||||||
|
--text-info-color-gradient-end: hsl(191, 98.2%, 56.1%);
|
||||||
|
|
||||||
|
--text-success-color: hsl(86, 81.4%, 46.3%);
|
||||||
|
--text-success-color-gradient-start: hsl(133, 76.5%, 38.4%);
|
||||||
|
--text-success-color-gradient-end: hsl(72, 81.1%, 52.4%);
|
||||||
|
|
||||||
|
--text-warning-color: hsl(47, 96.2%, 59.2%);
|
||||||
|
--text-warning-color-gradient-start: hsl(0, 90.4%, 59.2%);
|
||||||
|
--text-warning-color-gradient-end: hsl(47, 96.2%, 59.2%);
|
||||||
|
|
||||||
|
--text-danger-color: hsl(0, 95%, 47.1%);
|
||||||
|
--text-danger-color-gradient-start: hsl(0, 92.8%, 43.5%);
|
||||||
|
--text-danger-color-gradient-end: hsl(344, 100%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion) {
|
||||||
|
*, *::before, *::after {
|
||||||
|
animation-duration: 0s !important;
|
||||||
|
/* additional recommendation */
|
||||||
|
transition: none !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
background-image: linear-gradient(100deg, hsl(0, 0%, 98%), hsl(240, 5.9%, 90%));
|
||||||
|
color: hsl(240, 5.9%, 10%);
|
||||||
|
/*color: lch(8.35%, 2.25, 285.92);*/
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
min-height: 100dvh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
scrollbar-gutter: stable both-edges;
|
scrollbar-gutter: stable both-edges;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.1;
|
||||||
|
|
||||||
|
/* these will clamp the width between the two values
|
||||||
|
and keep it at 90% of the max when between them */
|
||||||
|
max-width: clamp(320px, 90%, 1280px);
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
transition-duration: 0.05s;
|
transition-duration: 0.05s;
|
||||||
transition-timing-function: ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
transition-property: background, color;
|
transition-property: background, color;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
button, a {
|
button, a {
|
||||||
@ -49,6 +90,16 @@ nav {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-style: italic;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
shape-margin: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1023px) {
|
@media (max-width: 1023px) {
|
||||||
.grid-container {
|
.grid-container {
|
||||||
@apply grid-cols-1;
|
@apply grid-cols-1;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
@apply uppercase font-semibold text-xs text-zinc-600 dark:text-zinc-300;
|
@apply uppercase font-semibold text-xs text-zinc-600 dark:text-zinc-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.form-input[type="date"],
|
input.form-input[type="date"],
|
||||||
@ -67,5 +67,5 @@ input.form-input[type="url"]:disabled,
|
|||||||
input.form-input[type="week"]:disabled,
|
input.form-input[type="week"]:disabled,
|
||||||
textarea.form-input:disabled,
|
textarea.form-input:disabled,
|
||||||
select.form-input:disabled {
|
select.form-input:disabled {
|
||||||
@apply bg-neutral-400 opacity-7;
|
@apply bg-neutral-400 opacity-75;
|
||||||
}
|
}
|
||||||
|
106
src/resources/css/elements/glossy-buttons.css
Normal file
106
src/resources/css/elements/glossy-buttons.css
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
gap: 4em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-button {
|
||||||
|
--h: 196;
|
||||||
|
--s: 100%;
|
||||||
|
--l: 41%;
|
||||||
|
}
|
||||||
|
.secondary-button {
|
||||||
|
--h: 28;
|
||||||
|
--s: 100%;
|
||||||
|
--l: 41%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-button {
|
||||||
|
font-size: clamp(1.2rem, 5vw + 1rem, 2.5rem);
|
||||||
|
width: 10em;
|
||||||
|
height: 4em;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background-image: linear-gradient(#0003,#0000);
|
||||||
|
box-shadow:
|
||||||
|
0 -0.125em 0.25em #0002,
|
||||||
|
0 0.25em 0.25em hsl(var(--h) var(--s) var(--l) / 0.5),
|
||||||
|
0 0 0 0.1em hsl(var(--h) var(--s) var(--l) / 0.5),
|
||||||
|
0 0.175em 0.3em hsl(var(--h) var(--s) var(--l) / 0.5) inset,
|
||||||
|
0 -0.025em 0.175em hsl(var(--h) var(--s) var(--l) / 0.4) inset,
|
||||||
|
0 -0.25em 1em 0.3em hsl(var(--h) var(--s) var(--l) / 0.3) inset,
|
||||||
|
0 0.6em 0 hsl(var(--h) var(--s) var(--l) / 0.3) inset,
|
||||||
|
0 2em 1em #0004;
|
||||||
|
backdrop-filter: blur(0.15em);
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
color: hsl(var(--h) var(--s) var(--l) / .7);
|
||||||
|
text-shadow:
|
||||||
|
0.03em 0.03em #fff5,
|
||||||
|
-0.03em -0.03em #0005;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.1s ease;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-button:before {
|
||||||
|
content: '';
|
||||||
|
height: 1.5em;
|
||||||
|
left: 10%;
|
||||||
|
top: 100%;
|
||||||
|
position: absolute;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-button:after {
|
||||||
|
content: '';
|
||||||
|
inset: 0;
|
||||||
|
top: 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(
|
||||||
|
105deg,
|
||||||
|
transparent 20%,
|
||||||
|
hsl(var(--h) var(--s) var(--l) / .2) 20%,
|
||||||
|
hsl(var(--h) var(--s) var(--l) / .2) 30%,
|
||||||
|
transparent 30%,
|
||||||
|
transparent 32%,
|
||||||
|
hsl(var(--h) var(--s) var(--l) / .2) 5%,
|
||||||
|
hsl(var(--h) var(--s) var(--l) / .2) 40%,
|
||||||
|
transparent 0%
|
||||||
|
);
|
||||||
|
background-size: 400% 100%;
|
||||||
|
background-position: 100% 0%;
|
||||||
|
transition: .3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-button:active:after {
|
||||||
|
background-position: -50% 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-button:active {
|
||||||
|
backdrop-filter: blur(0.08em);
|
||||||
|
box-shadow:
|
||||||
|
0 -0.125em 0.25em #0002,
|
||||||
|
0 0.25em 0.25em hsl(var(--h) var(--s) var(--l) / 0.5),
|
||||||
|
0 0 0 0.1em hsl(var(--h) var(--s) var(--l) / 0.5),
|
||||||
|
0 0.175em 0.3em hsl(var(--h) var(--s) var(--l) / 0.8) inset,
|
||||||
|
0 -0.025em 0.175em hsl(var(--h) var(--s) var(--l) / 0.4) inset,
|
||||||
|
0 -0.25em 1em 0.3em hsl(var(--h) var(--s) var(--l) / 0.3) inset,
|
||||||
|
0 0.6em 0 hsl(var(--h) var(--s) var(--l) / 0.3) inset,
|
||||||
|
0 1em 0.5em #0004;
|
||||||
|
translate: .01em .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-button:active:before {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*body {
|
||||||
|
background-color: #3d3d3d;
|
||||||
|
display: grid;
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
place-content: center;
|
||||||
|
}*/
|
74
src/resources/css/resets/andy-bell.reset.css
Normal file
74
src/resources/css/resets/andy-bell.reset.css
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/* Box sizing rules */
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove default margin */
|
||||||
|
body,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
p,
|
||||||
|
figure,
|
||||||
|
blockquote,
|
||||||
|
dl,
|
||||||
|
dd {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
|
||||||
|
ul[role='list'],
|
||||||
|
ol[role='list'] {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set core root defaults */
|
||||||
|
html:focus-within {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set core body defaults */
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
text-rendering: optimizeSpeed;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A elements that don't have a class get default styles */
|
||||||
|
a:not([class]) {
|
||||||
|
text-decoration-skip-ink: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make images easier to work with */
|
||||||
|
img,
|
||||||
|
picture {
|
||||||
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inherit fonts for inputs and buttons */
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
html:focus-within {
|
||||||
|
scroll-behavior: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
}
|
||||||
|
}
|
48
src/resources/css/resets/eric-meyer.reset.css
Normal file
48
src/resources/css/resets/eric-meyer.reset.css
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
349
src/resources/css/resets/nicolas-allagher.normalize.css
Normal file
349
src/resources/css/resets/nicolas-allagher.normalize.css
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -3,6 +3,70 @@
|
|||||||
/** +--------------------------------+ **/
|
/** +--------------------------------+ **/
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-Regular.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-Italic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-Medium.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-MediumItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-SemiBold.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-SemiBoldItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-Bold.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "BarlowCondensed";
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/BarlowCondensed/BarlowCondensed-BoldItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@font-face {
|
||||||
font-family: "Lato";
|
font-family: "Lato";
|
||||||
src: url('/fonts/Lato/Lato-Regular.woff2') format("woff2");
|
src: url('/fonts/Lato/Lato-Regular.woff2') format("woff2");
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -48,13 +112,77 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/** +--------------------------------+ **/
|
/** +--------------------------------+ **/
|
||||||
/** | Serif fonts | **/
|
/** | Serif fonts | **/
|
||||||
/** +--------------------------------+ **/
|
/** +--------------------------------+ **/
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-Regular.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-Italic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-Medium.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-MediumItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-SemiBold.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-SemiBoldItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-Bold.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Montserrat";
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("/fonts/Montserrat/Montserrat-BoldItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@font-face {
|
||||||
font-family: "Nunito";
|
font-family: "Nunito";
|
||||||
src: url('/fonts/Nunito/Nunito-Regular.woff2') format("woff2");
|
src: url('/fonts/Nunito/Nunito-Regular.woff2') format("woff2");
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -118,7 +246,7 @@
|
|||||||
font-stretch: 75% 125%;
|
font-stretch: 75% 125%;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/*@font-face {
|
/*@font-face {
|
||||||
font-family: "Raleway";
|
font-family: "Raleway";
|
||||||
|
45
src/resources/css/typography/text-gradient.css
Normal file
45
src/resources/css/typography/text-gradient.css
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
.text-gradient {
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient.text-primary {
|
||||||
|
background-image: linear-gradient(310deg, var(--primary-text-color-gradient-start), var(--primary-text-color-gradient-end));
|
||||||
|
}
|
||||||
|
.text-primary {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient.text-info {
|
||||||
|
background-image: linear-gradient(310deg, var(--text-info-color-gradient-start), var(--text-info-color-gradient-end));
|
||||||
|
}
|
||||||
|
.text-info {
|
||||||
|
color: var(--text-info-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient.text-success {
|
||||||
|
background-image: linear-gradient(310deg, var(--text-success-color-gradient-start), var(--text-success-color-gradient-end));
|
||||||
|
color: hsl(86, 81.4%, 46.3%);
|
||||||
|
}
|
||||||
|
.text-success {
|
||||||
|
color: var(--text-success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient.text-warning {
|
||||||
|
background-image: linear-gradient(310deg, var(--text-warning-color-gradient-start), var(--text-warning-color-gradient-end));
|
||||||
|
color: hsl(47, 96.2%, 59.2%);
|
||||||
|
}
|
||||||
|
.text-warning {
|
||||||
|
color: var(--text-warning-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient.text-danger {
|
||||||
|
background-image: linear-gradient(310deg, var(--text-danger-color-gradient-start), var(--text-danger-color-gradient-end));
|
||||||
|
color: hsl(0, 95%, 47.1%);
|
||||||
|
}
|
||||||
|
.text-danger {
|
||||||
|
color: var(--text-danger-color);
|
||||||
|
}
|
@ -1,3 +1,30 @@
|
|||||||
.button-row {
|
.button-row {
|
||||||
@apply inline-grid grid-flow-col auto-cols-max items-center; /* intentionally leaving gap out so that it can be set on per-case basis */
|
@apply inline-grid grid-flow-col auto-cols-max items-center; /* intentionally leaving gap out so that it can be set on per-case basis */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gradBorder i {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 30px;
|
||||||
|
padding: 1.5px; /* the thickness of the border */
|
||||||
|
/* the below will do the magic */
|
||||||
|
mask:
|
||||||
|
linear-gradient(#fff 0 0) content-box,
|
||||||
|
/* this will cover only the content area (no padding) */
|
||||||
|
linear-gradient(#fff 0 0); /* this will cover all the area */
|
||||||
|
-webkit-mask-composite: xor; /* needed for old browsers until the below is more supported */
|
||||||
|
mask-composite: exclude; /* this will exclude the first layer from the second so only the padding area will be kept visible */
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradBorder i::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(180deg, #ffffff73 0%, #ffffff10 50%);
|
||||||
|
transition: transform 0.7s linear;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,108 @@
|
|||||||
:root {
|
:root {
|
||||||
/**/
|
|
||||||
|
/** ------------------------------
|
||||||
|
* START -- https://coolors.co/022f40-38aecc-0090c1-183446-046e8f
|
||||||
|
* -------------------------------**/
|
||||||
|
/* CSS HEX */
|
||||||
|
--prussian-blue: #022f40ff;
|
||||||
|
--pacific-cyan: #38aeccff;
|
||||||
|
--blue-ncs: #0090c1ff;
|
||||||
|
--prussian-blue-2: #183446ff;
|
||||||
|
--cerulean: #046e8fff;
|
||||||
|
|
||||||
|
/* CSS HSL */
|
||||||
|
--prussian-blue: hsla(196, 94%, 13%, 1);
|
||||||
|
--pacific-cyan: hsla(192, 59%, 51%, 1);
|
||||||
|
--blue-ncs: hsla(195, 100%, 38%, 1);
|
||||||
|
--prussian-blue-2: hsla(203, 49%, 18%, 1);
|
||||||
|
--cerulean: hsla(194, 95%, 29%, 1);
|
||||||
|
|
||||||
|
/* SCSS HEX */
|
||||||
|
$prussian-blue: #022f40ff;
|
||||||
|
$pacific-cyan: #38aeccff;
|
||||||
|
$blue-ncs: #0090c1ff;
|
||||||
|
$prussian-blue-2: #183446ff;
|
||||||
|
$cerulean: #046e8fff;
|
||||||
|
|
||||||
|
/* SCSS HSL */
|
||||||
|
$prussian-blue: hsla(196, 94%, 13%, 1);
|
||||||
|
$pacific-cyan: hsla(192, 59%, 51%, 1);
|
||||||
|
$blue-ncs: hsla(195, 100%, 38%, 1);
|
||||||
|
$prussian-blue-2: hsla(203, 49%, 18%, 1);
|
||||||
|
$cerulean: hsla(194, 95%, 29%, 1);
|
||||||
|
|
||||||
|
/* SCSS RGB */
|
||||||
|
$prussian-blue: rgba(2, 47, 64, 1);
|
||||||
|
$pacific-cyan: rgba(56, 174, 204, 1);
|
||||||
|
$blue-ncs: rgba(0, 144, 193, 1);
|
||||||
|
$prussian-blue-2: rgba(24, 52, 70, 1);
|
||||||
|
$cerulean: rgba(4, 110, 143, 1);
|
||||||
|
|
||||||
|
/* SCSS Gradient */
|
||||||
|
/*$gradient-top: linear-gradient(0deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-right: linear-gradient(90deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-bottom: linear-gradient(180deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-left: linear-gradient(270deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-top-right: linear-gradient(45deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-bottom-right: linear-gradient(135deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-top-left: linear-gradient(225deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-bottom-left: linear-gradient(315deg, #022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/*$gradient-radial: radial-gradient(#022f40ff, #38aeccff, #0090c1ff, #183446ff, #046e8fff);*/
|
||||||
|
/** ------------------------------
|
||||||
|
* END -- https://coolors.co/022f40-38aecc-0090c1-183446-046e8f
|
||||||
|
* -------------------------------**/
|
||||||
|
|
||||||
|
|
||||||
|
/** ------------------------------
|
||||||
|
* START -- https://coolors.co/8ac482-47783f-ffbe86-ffb5c2-3777ff
|
||||||
|
* -------------------------------**/
|
||||||
|
/* CSS HEX */
|
||||||
|
--pistachio: #8ac482ff;
|
||||||
|
--fern-green: #47783fff;
|
||||||
|
--peach: #ffbe86ff;
|
||||||
|
--cherry-blossom-pink: #ffb5c2ff;
|
||||||
|
--blue-crayola: #3777ffff;
|
||||||
|
|
||||||
|
/* CSS HSL */
|
||||||
|
--pistachio: hsla(113, 36%, 64%, 1);
|
||||||
|
--fern-green: hsla(112, 31%, 36%, 1);
|
||||||
|
--peach: hsla(28, 100%, 76%, 1);
|
||||||
|
--cherry-blossom-pink: hsla(349, 100%, 85%, 1);
|
||||||
|
--blue-crayola: hsla(221, 100%, 61%, 1);
|
||||||
|
|
||||||
|
/* SCSS HEX */
|
||||||
|
$pistachio: #8ac482ff;
|
||||||
|
$fern-green: #47783fff;
|
||||||
|
$peach: #ffbe86ff;
|
||||||
|
$cherry-blossom-pink: #ffb5c2ff;
|
||||||
|
$blue-crayola: #3777ffff;
|
||||||
|
|
||||||
|
/* SCSS HSL */
|
||||||
|
$pistachio: hsla(113, 36%, 64%, 1);
|
||||||
|
$fern-green: hsla(112, 31%, 36%, 1);
|
||||||
|
$peach: hsla(28, 100%, 76%, 1);
|
||||||
|
$cherry-blossom-pink: hsla(349, 100%, 85%, 1);
|
||||||
|
$blue-crayola: hsla(221, 100%, 61%, 1);
|
||||||
|
|
||||||
|
/* SCSS RGB */
|
||||||
|
$pistachio: rgba(138, 196, 130, 1);
|
||||||
|
$fern-green: rgba(71, 120, 63, 1);
|
||||||
|
$peach: rgba(255, 190, 134, 1);
|
||||||
|
$cherry-blossom-pink: rgba(255, 181, 194, 1);
|
||||||
|
$blue-crayola: rgba(55, 119, 255, 1);
|
||||||
|
|
||||||
|
/* SCSS Gradient */
|
||||||
|
/*$gradient-top: linear-gradient(0deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-right: linear-gradient(90deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-bottom: linear-gradient(180deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-left: linear-gradient(270deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-top-right: linear-gradient(45deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-bottom-right: linear-gradient(135deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-top-left: linear-gradient(225deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-bottom-left: linear-gradient(315deg, #8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/*$gradient-radial: radial-gradient(#8ac482ff, #47783fff, #ffbe86ff, #ffb5c2ff, #3777ffff);*/
|
||||||
|
/** ------------------------------
|
||||||
|
* END -- https://coolors.co/8ac482-47783f-ffbe86-ffb5c2-3777ff
|
||||||
|
* -------------------------------**/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
93
src/resources/js/Components/BellNotifications.vue
Normal file
93
src/resources/js/Components/BellNotifications.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { usePage } from '@inertiajs/inertia-vue3'
|
||||||
|
import Dropdown from './Dropdown.vue'
|
||||||
|
import GhostButton from '@/Components/Buttons/GhostButton.vue'
|
||||||
|
import IconBell from '@/Icons/Bell.vue'
|
||||||
|
import Modal from '@/Components/Modals/Modal.vue'
|
||||||
|
|
||||||
|
// variables
|
||||||
|
const notifications = ref(usePage().props.value.notifications)
|
||||||
|
const hasUnreadNotifications = ref(usePage().props.value.unreadNotifications)
|
||||||
|
const showNotification = ref(false)
|
||||||
|
const activeNotification = ref({})
|
||||||
|
|
||||||
|
// methods
|
||||||
|
const openNotification = (notification) => {
|
||||||
|
activeNotification.value = notification
|
||||||
|
showNotification.value = true
|
||||||
|
markActiveAsRead()
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
showNotification.value = false
|
||||||
|
activeNotification.value = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const markActiveAsRead = () => {
|
||||||
|
axios.post(route('api.notifications.mark-read', activeNotification.value.id))
|
||||||
|
.then(resp => {
|
||||||
|
activeNotification.value.read_at = resp.data.read_at
|
||||||
|
})
|
||||||
|
.catch(erro => {
|
||||||
|
//console.error(erro)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const markAllRead = () => {
|
||||||
|
axios.post(route('api.notifications.mark-all-read'))
|
||||||
|
.then(resp => {
|
||||||
|
let nowDt = new Date()
|
||||||
|
notifications.value.forEach(notification => {
|
||||||
|
if (notification.read_at === null) {
|
||||||
|
notification.read_at = nowDt
|
||||||
|
}
|
||||||
|
})
|
||||||
|
hasUnreadNotifications.value = false
|
||||||
|
})
|
||||||
|
.catch(erro => {
|
||||||
|
//console.error(erro)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dropdown align="right" width="80">
|
||||||
|
<template #trigger>
|
||||||
|
<GhostButton>
|
||||||
|
<IconBell></IconBell>
|
||||||
|
<div v-show="hasUnreadNotifications" class="absolute top-2 right-4 w-3 h-3 z-10 border-2 border-stone-100 bg-red-800 rounded-full"> </div>
|
||||||
|
</GhostButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="grid auto-rows-max gap-y-px bg-stone-400">
|
||||||
|
<template v-for="notification in notifications" :key="notification.id">
|
||||||
|
<div class="px-4 py-2 grid auto-rows-max gap-3 cursor-pointer" :class="{'bg-stone-200': notification.read_at !== null, 'bg-white': notification.read_at === null}" @click="openNotification(notification)">
|
||||||
|
<div>{{ notification.data.title }}</div>
|
||||||
|
<div class="text-xs text-zinc-600">{{ notification.data.created_at_date }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-t border-gray-100"></div>
|
||||||
|
|
||||||
|
<div class="px-2">
|
||||||
|
<button class="btn btn-ghost text-xs" @click="markAllRead">Mark All Read</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<Modal :show="showNotification" @close="closeModal">
|
||||||
|
<div class="grid auto-rows-max">
|
||||||
|
<header class="bg-sky-800 text-white p-4">{{ activeNotification.data.title }}</header>
|
||||||
|
<div class="p-4 bg-white text-zinc-900">
|
||||||
|
<p class="mb-4">{{ activeNotification.data.body }}</p>
|
||||||
|
|
||||||
|
<p class="text-sm">{{ activeNotification.data.type }} | {{ activeNotification.data.created_at_date }} at {{ activeNotification.data.created_at_time }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
32
src/resources/js/Components/Forms/CheckboxInput.vue
Normal file
32
src/resources/js/Components/Forms/CheckboxInput.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
// variables
|
||||||
|
const emit = defineEmits(['update:checked'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
checked: {
|
||||||
|
type: [Array, Boolean],
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed properties
|
||||||
|
const proxyChecked = computed({
|
||||||
|
get() {
|
||||||
|
return props.checked
|
||||||
|
},
|
||||||
|
|
||||||
|
set(val) {
|
||||||
|
emit('update:checked', val)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input type="checkbox" class="checkbox" :value="value" v-model="proxyChecked">
|
||||||
|
</template>
|
10
src/resources/js/Components/Forms/InputError.vue
Normal file
10
src/resources/js/Components/Forms/InputError.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script setup>
|
||||||
|
// defines
|
||||||
|
defineProps({
|
||||||
|
message: String,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p v-show="message" class="px-2 py-1 text-sm text-red-600">{{ message }}</p>
|
||||||
|
</template>
|
20
src/resources/js/Components/Forms/InputLabel.vue
Normal file
20
src/resources/js/Components/Forms/InputLabel.vue
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useSlots, computed } from 'vue'
|
||||||
|
|
||||||
|
// defines
|
||||||
|
defineProps({
|
||||||
|
value: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed properties
|
||||||
|
const hasActions = computed(() => !! useSlots().actions)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text"><slot></slot></span>
|
||||||
|
<span v-show="hasActions" class="flex items-center">
|
||||||
|
<slot name="actions"></slot>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</template>
|
28
src/resources/js/Components/Forms/RadioInput.vue
Normal file
28
src/resources/js/Components/Forms/RadioInput.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
// variables
|
||||||
|
const emit = defineEmits(['update:checked'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
checked: {
|
||||||
|
type: [Array, Boolean],
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed properties
|
||||||
|
const proxyChecked = computed({
|
||||||
|
get() {
|
||||||
|
return props.checked
|
||||||
|
},
|
||||||
|
|
||||||
|
set(val) {
|
||||||
|
emit('update:checked', val)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input type="radio" class="radio" v-model="proxyChecked">
|
||||||
|
</template>
|
28
src/resources/js/Components/Forms/TextInput.vue
Normal file
28
src/resources/js/Components/Forms/TextInput.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
// defines
|
||||||
|
defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
modelValue: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus: () => input.value.focus()
|
||||||
|
})
|
||||||
|
|
||||||
|
// variables
|
||||||
|
const input = ref(null)
|
||||||
|
|
||||||
|
// lifecycle hooks
|
||||||
|
onMounted(() => {
|
||||||
|
if (input.value.hasAttribute('autofocus')) {
|
||||||
|
input.value.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input ref="input" class="input" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
|
||||||
|
</template>
|
30
src/resources/js/Components/Forms/Textarea.vue
Normal file
30
src/resources/js/Components/Forms/Textarea.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
// defines
|
||||||
|
defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
modelValue: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus: () => input.value.focus()
|
||||||
|
})
|
||||||
|
|
||||||
|
// variables
|
||||||
|
const input = ref(null)
|
||||||
|
|
||||||
|
// lifecycle hooks
|
||||||
|
onMounted(() => {
|
||||||
|
if (input.value.hasAttribute('autofocus')) {
|
||||||
|
input.value.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<textarea ref="input" class="textarea" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
|
||||||
|
<slot></slot>
|
||||||
|
</textarea>
|
||||||
|
</template>
|
52
src/resources/js/Components/LanguageSwitcher.vue
Normal file
52
src/resources/js/Components/LanguageSwitcher.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Inertia } from '@inertiajs/inertia'
|
||||||
|
import { usePage } from '@inertiajs/inertia-vue3'
|
||||||
|
import Dropdown from './DropdownMenu.vue'
|
||||||
|
|
||||||
|
// variables
|
||||||
|
const currentLocale = ref(usePage().props.value.currentLocale)
|
||||||
|
|
||||||
|
// methods
|
||||||
|
const isCurrent = (language) => {
|
||||||
|
return language.iso_code === currentLocale.value.iso_code
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeLocale = (language) => {
|
||||||
|
if (language.iso_code !== currentLocale.value.iso_code) {
|
||||||
|
Inertia.post(route('locale.set'), language, {
|
||||||
|
replace: true,
|
||||||
|
onSuccess: page => {
|
||||||
|
currentLocale.value = page.props.currentLocale
|
||||||
|
},
|
||||||
|
onError: errors => {
|
||||||
|
console.log(errors)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dropdown align="right" width="60">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="py-2 px-4 inline-grid grid-flow-col auto-cols-max gap-x-2 items-center bg-white hover:bg-gray-50 text-sm leading-4 font-medium rounded-md cursor-pointer">
|
||||||
|
<svg class="h-4 w-8">
|
||||||
|
<use :href="`#flag-${currentLocale.iso_code}`"></use>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2">{{ currentLocale.localized_name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<div class="w-60">
|
||||||
|
<button type="button" v-for="(lang, langIdx) in $page.props.availableLocales" :key="langIdx" @click="changeLocale(lang)" class="py-2 px-4 inline-grid grid-flow-col auto-cols-max gap-x-2 items-center w-full" :class="{ 'text-white bg-sky-600 cursor-default': isCurrent(lang), 'hover:bg-sky-600/50': !isCurrent(lang) }">
|
||||||
|
<svg class="h-4 w-8">
|
||||||
|
<use :href="`#flag-${lang.iso_code}`"></use>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2">{{ lang.localized_name }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</template>
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<circle cx="16" cy="4" r="1"></circle>
|
<circle cx="16" cy="4" r="1"></circle>
|
||||||
<path d="m18 19 1-7-5.87.94"></path>
|
<path d="m18 19 1-7-5.87.94"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
|
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
|
<path d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
|
||||||
<path d="M6 8h12"></path>
|
<path d="M6 8h12"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1"></path>
|
<path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1"></path>
|
||||||
<polygon points="12 15 17 21 7 21 12 15"></polygon>
|
<polygon points="12 15 17 21 7 21 12 15"></polygon>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M12 21a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
|
<path d="M12 21a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
|
||||||
<path d="M5 3 2 6"></path>
|
<path d="M5 3 2 6"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<circle cx="12" cy="13" r="8"></circle>
|
<circle cx="12" cy="13" r="8"></circle>
|
||||||
<path d="M12 9v4l2 2"></path>
|
<path d="M12 9v4l2 2"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M6.87 6.87a8 8 0 1 0 11.26 11.26"></path>
|
<path d="M6.87 6.87a8 8 0 1 0 11.26 11.26"></path>
|
||||||
<path d="M19.9 14.25A7.44 7.44 0 0 0 20 13a8 8 0 0 0-8-8 7.44 7.44 0 0 0-1.25.1"></path>
|
<path d="M19.9 14.25A7.44 7.44 0 0 0 20 13a8 8 0 0 0-8-8 7.44 7.44 0 0 0-1.25.1"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M12 21a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
|
<path d="M12 21a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
|
||||||
<path d="M5 3 2 6"></path>
|
<path d="M5 3 2 6"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M12 21a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
|
<path d="M12 21a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"></path>
|
||||||
<path d="M5 3 2 6"></path>
|
<path d="M5 3 2 6"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||||
<polyline points="11 3 11 11 14 8 17 11 17 3"></polyline>
|
<polyline points="11 3 11 11 14 8 17 11 17 3"></polyline>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<circle cx="12" cy="12" r="10"></circle>
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
<line x1="12" y1="8" x2="12" y2="12"></line>
|
<line x1="12" y1="8" x2="12" y2="12"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon>
|
<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon>
|
||||||
<line x1="12" y1="8" x2="12" y2="12"></line>
|
<line x1="12" y1="8" x2="12" y2="12"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
|
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
|
||||||
<line x1="12" y1="9" x2="12" y2="13"></line>
|
<line x1="12" y1="9" x2="12" y2="13"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<line x1="21" y1="6" x2="3" y2="6"></line>
|
<line x1="21" y1="6" x2="3" y2="6"></line>
|
||||||
<line x1="17" y1="12" x2="7" y2="12"></line>
|
<line x1="17" y1="12" x2="7" y2="12"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M2 12h20"></path>
|
<path d="M2 12h20"></path>
|
||||||
<path d="M10 16v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-4"></path>
|
<path d="M10 16v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-4"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<path d="M12 2v20"></path>
|
<path d="M12 2v20"></path>
|
||||||
<path d="M8 10H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h4"></path>
|
<path d="M8 10H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h4"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="4" y="2" width="6" height="16" rx="2"></rect>
|
<rect x="4" y="2" width="6" height="16" rx="2"></rect>
|
||||||
<rect x="14" y="9" width="6" height="9" rx="2"></rect>
|
<rect x="14" y="9" width="6" height="9" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="2" y="4" width="16" height="6" rx="2"></rect>
|
<rect x="2" y="4" width="16" height="6" rx="2"></rect>
|
||||||
<rect x="9" y="14" width="9" height="6" rx="2"></rect>
|
<rect x="9" y="14" width="9" height="6" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="4" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="4" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="14" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="14" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="4" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="4" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="14" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="14" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="4" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="4" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="14" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="14" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="2" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="2" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="16" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="16" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="2" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="2" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="12" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="12" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="6" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="6" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="16" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="16" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="9" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="9" y="7" width="6" height="10" rx="2"></rect>
|
||||||
<path d="M4 22V2"></path>
|
<path d="M4 22V2"></path>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="3" y="5" width="6" height="14" rx="2"></rect>
|
<rect x="3" y="5" width="6" height="14" rx="2"></rect>
|
||||||
<rect x="15" y="7" width="6" height="10" rx="2"></rect>
|
<rect x="15" y="7" width="6" height="10" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<line x1="21" y1="6" x2="3" y2="6"></line>
|
<line x1="21" y1="6" x2="3" y2="6"></line>
|
||||||
<line x1="15" y1="12" x2="3" y2="12"></line>
|
<line x1="15" y1="12" x2="3" y2="12"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<line x1="21" y1="6" x2="3" y2="6"></line>
|
<line x1="21" y1="6" x2="3" y2="6"></line>
|
||||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="4" y="6" width="6" height="16" rx="2"></rect>
|
<rect x="4" y="6" width="6" height="16" rx="2"></rect>
|
||||||
<rect x="14" y="6" width="6" height="9" rx="2"></rect>
|
<rect x="14" y="6" width="6" height="9" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="6" y="14" width="9" height="6" rx="2"></rect>
|
<rect x="6" y="14" width="9" height="6" rx="2"></rect>
|
||||||
<rect x="6" y="4" width="16" height="6" rx="2"></rect>
|
<rect x="6" y="4" width="16" height="6" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="5" y="14" width="14" height="6" rx="2"></rect>
|
<rect x="5" y="14" width="14" height="6" rx="2"></rect>
|
||||||
<rect x="7" y="4" width="10" height="6" rx="2"></rect>
|
<rect x="7" y="4" width="10" height="6" rx="2"></rect>
|
||||||
|
@ -33,7 +33,7 @@ const titleString = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
<svg xmlns="http://www.w3.org/2000/svg" :height="heightNumber" :width="widthNumber" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" preserveAspectRatio="xMidYMid meet">
|
||||||
<title v-if="titleString.length > 0" v-html="title"></title>
|
<title v-if="titleString.length > 0" v-html="title"></title>
|
||||||
<rect x="5" y="14" width="14" height="6" rx="2"></rect>
|
<rect x="5" y="14" width="14" height="6" rx="2"></rect>
|
||||||
<rect x="7" y="4" width="10" height="6" rx="2"></rect>
|
<rect x="7" y="4" width="10" height="6" rx="2"></rect>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user