Compare commits
4 Commits
9957e8d8a1
...
7a2b0e44ad
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a2b0e44ad | |||
| 50b9cfe17d | |||
| c7ac146fab | |||
| 7ba5041ba6 |
@ -20,9 +20,9 @@ class PasswordController extends Controller
|
|||||||
'password' => ['required', Password::defaults(), 'confirmed'],
|
'password' => ['required', Password::defaults(), 'confirmed'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$request->user()->update([
|
$request->user()->forceFill([
|
||||||
'password' => Hash::make($validated['password']),
|
'password' => Hash::make($validated['password']),
|
||||||
]);
|
])->save();
|
||||||
|
|
||||||
return back()->with('status', 'password-updated');
|
return back()->with('status', 'password-updated');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,11 +35,11 @@ class RegisteredUserController extends Controller
|
|||||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = User::create([
|
$user = new User();
|
||||||
'name' => $request->name,
|
$user->name = $request->name;
|
||||||
'email' => $request->email,
|
$user->email = $request->email;
|
||||||
'password' => Hash::make($request->password),
|
$user->password = $request->password;
|
||||||
]);
|
$user->save();
|
||||||
|
|
||||||
event(new Registered($user));
|
event(new Registered($user));
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class User extends Authenticatable
|
|||||||
'firstname',
|
'firstname',
|
||||||
'lastname',
|
'lastname',
|
||||||
'displayname',
|
'displayname',
|
||||||
|
'name',
|
||||||
'email',
|
'email',
|
||||||
'timezone',
|
'timezone',
|
||||||
'phone',
|
'phone',
|
||||||
@ -59,6 +60,62 @@ class User extends Authenticatable
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose a Breeze-compatible "name" attribute without a physical column.
|
||||||
|
* Preference: displayname (explicit override), then first + last, then email.
|
||||||
|
*/
|
||||||
|
public function getNameAttribute(): string
|
||||||
|
{
|
||||||
|
$displayname = is_string($this->displayname) ? trim($this->displayname) : '';
|
||||||
|
if ($displayname !== '') {
|
||||||
|
return $displayname;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = is_string($this->firstname) ? trim($this->firstname) : '';
|
||||||
|
$last = is_string($this->lastname) ? trim($this->lastname) : '';
|
||||||
|
$full = trim($first . ' ' . $last);
|
||||||
|
|
||||||
|
if ($full !== '') {
|
||||||
|
return $full;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) ($this->email ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map "name" writes to first/last names, keeping displayname optional.
|
||||||
|
*/
|
||||||
|
public function setNameAttribute(?string $value): void
|
||||||
|
{
|
||||||
|
$incoming = trim((string) $value);
|
||||||
|
|
||||||
|
$currentFirst = is_string($this->attributes['firstname'] ?? null)
|
||||||
|
? trim((string) $this->attributes['firstname'])
|
||||||
|
: '';
|
||||||
|
$currentLast = is_string($this->attributes['lastname'] ?? null)
|
||||||
|
? trim((string) $this->attributes['lastname'])
|
||||||
|
: '';
|
||||||
|
$currentGenerated = trim($currentFirst . ' ' . $currentLast);
|
||||||
|
|
||||||
|
if ($incoming === '') {
|
||||||
|
$this->attributes['firstname'] = null;
|
||||||
|
$this->attributes['lastname'] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = preg_split('/\s+/', $incoming, 2);
|
||||||
|
$this->attributes['firstname'] = $parts[0] ?? null;
|
||||||
|
$this->attributes['lastname'] = $parts[1] ?? null;
|
||||||
|
|
||||||
|
$displayname = is_string($this->attributes['displayname'] ?? null)
|
||||||
|
? trim((string) $this->attributes['displayname'])
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if ($displayname !== '' && $displayname === $currentGenerated) {
|
||||||
|
$this->attributes['displayname'] = $incoming;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* user can own many calendars
|
* user can own many calendars
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -28,7 +28,7 @@ class CalendarViewBuilder
|
|||||||
$gridStartMinutes = $daytimeHours ? ((int) $daytimeHours['start'] * 60) : 0;
|
$gridStartMinutes = $daytimeHours ? ((int) $daytimeHours['start'] * 60) : 0;
|
||||||
$gridEndMinutes = $daytimeHours ? ((int) $daytimeHours['end'] * 60) : (24 * 60);
|
$gridEndMinutes = $daytimeHours ? ((int) $daytimeHours['end'] * 60) : (24 * 60);
|
||||||
|
|
||||||
return $events->flatMap(function ($e) use (
|
$payloads = $events->flatMap(function ($e) use (
|
||||||
$calendarMap,
|
$calendarMap,
|
||||||
$uiFormat,
|
$uiFormat,
|
||||||
$view,
|
$view,
|
||||||
@ -151,7 +151,12 @@ class CalendarViewBuilder
|
|||||||
'duration' => $placement['duration'],
|
'duration' => $placement['duration'],
|
||||||
];
|
];
|
||||||
})->filter()->values();
|
})->filter()->values();
|
||||||
})->keyBy('occurrence_id');
|
})->filter();
|
||||||
|
|
||||||
|
// ensure chronological ordering across calendars for all views
|
||||||
|
return $payloads
|
||||||
|
->sortBy('start')
|
||||||
|
->keyBy('occurrence_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
@ -12,26 +13,38 @@ return new class extends Migration
|
|||||||
return base_path("vendor/sabre/dav/examples/sql/{$file}");
|
return base_path("vendor/sabre/dav/examples/sql/{$file}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function prefix(): string
|
||||||
|
{
|
||||||
|
$driver = DB::connection()->getDriverName();
|
||||||
|
|
||||||
|
return match ($driver) {
|
||||||
|
'sqlite' => 'sqlite',
|
||||||
|
'pgsql' => 'pgsql',
|
||||||
|
default => 'mysql',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
// Disable FK checks for smooth batch execution
|
$prefix = $this->prefix();
|
||||||
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
Schema::disableForeignKeyConstraints();
|
||||||
|
|
||||||
// Principals (users & groups)
|
// Principals (users & groups)
|
||||||
DB::unprepared(File::get($this->sql('mysql.principals.sql')));
|
DB::unprepared(File::get($this->sql("{$prefix}.principals.sql")));
|
||||||
|
|
||||||
// CalDAV calendars + objects
|
// CalDAV calendars + objects
|
||||||
DB::unprepared(File::get($this->sql('mysql.calendars.sql')));
|
DB::unprepared(File::get($this->sql("{$prefix}.calendars.sql")));
|
||||||
|
|
||||||
// CardDAV address books + cards
|
// CardDAV address books + cards
|
||||||
DB::unprepared(File::get($this->sql('mysql.addressbooks.sql')));
|
DB::unprepared(File::get($this->sql("{$prefix}.addressbooks.sql")));
|
||||||
|
|
||||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
Schema::enableForeignKeyConstraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
{
|
{
|
||||||
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
$this->prefix();
|
||||||
|
Schema::disableForeignKeyConstraints();
|
||||||
|
|
||||||
// Drop in reverse dependency order
|
// Drop in reverse dependency order
|
||||||
DB::statement('DROP TABLE IF EXISTS
|
DB::statement('DROP TABLE IF EXISTS
|
||||||
@ -47,6 +60,6 @@ return new class extends Migration
|
|||||||
groupmembers
|
groupmembers
|
||||||
');
|
');
|
||||||
|
|
||||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
Schema::enableForeignKeyConstraints();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,7 +9,9 @@ return new class extends Migration
|
|||||||
// add composite + geo + optional fulltext indexes to locations
|
// add composite + geo + optional fulltext indexes to locations
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('locations', function (Blueprint $table) {
|
$driver = Schema::getConnection()->getDriverName();
|
||||||
|
|
||||||
|
Schema::table('locations', function (Blueprint $table) use ($driver) {
|
||||||
// composite btree index for common lookups
|
// composite btree index for common lookups
|
||||||
$table->index(
|
$table->index(
|
||||||
['display_name', 'city', 'state', 'postal', 'country'],
|
['display_name', 'city', 'state', 'postal', 'country'],
|
||||||
@ -21,17 +23,23 @@ return new class extends Migration
|
|||||||
|
|
||||||
// optional: fulltext index for free-form text searching
|
// optional: fulltext index for free-form text searching
|
||||||
// note: requires mysql/mariadb version with innodb fulltext support
|
// note: requires mysql/mariadb version with innodb fulltext support
|
||||||
$table->fullText('raw_address', 'locations_raw_address_fulltext');
|
if (in_array($driver, ['mysql', 'pgsql'], true)) {
|
||||||
|
$table->fullText('raw_address', 'locations_raw_address_fulltext');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop the indexes added in up()
|
// drop the indexes added in up()
|
||||||
public function down(): void
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::table('locations', function (Blueprint $table) {
|
$driver = Schema::getConnection()->getDriverName();
|
||||||
|
|
||||||
|
Schema::table('locations', function (Blueprint $table) use ($driver) {
|
||||||
$table->dropIndex('locations_name_city_idx');
|
$table->dropIndex('locations_name_city_idx');
|
||||||
$table->dropIndex('locations_lat_lon_idx');
|
$table->dropIndex('locations_lat_lon_idx');
|
||||||
$table->dropFullText('locations_raw_address_fulltext');
|
if (in_array($driver, ['mysql', 'pgsql'], true)) {
|
||||||
|
$table->dropFullText('locations_raw_address_fulltext');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -46,6 +46,35 @@ return [
|
|||||||
'saved' => 'Your calendar settings have been saved!',
|
'saved' => 'Your calendar settings have been saved!',
|
||||||
'title' => 'Calendar settings',
|
'title' => 'Calendar settings',
|
||||||
],
|
],
|
||||||
'timezone_help' => 'You can override your default time zone here.'
|
'timezone_help' => 'You can override your default time zone here.',
|
||||||
|
'toggle_sidebar' => 'Toggle calendar sidebar',
|
||||||
|
'event' => [
|
||||||
|
'when' => 'When',
|
||||||
|
'all_day' => 'All day',
|
||||||
|
'location' => 'Location',
|
||||||
|
'map_coming' => 'Map preview coming soon.',
|
||||||
|
'no_location' => 'No location set.',
|
||||||
|
'details' => 'Details',
|
||||||
|
'repeats' => 'Repeats',
|
||||||
|
'does_not_repeat' => 'Does not repeat',
|
||||||
|
'category' => 'Category',
|
||||||
|
'none' => 'None',
|
||||||
|
'visibility' => 'Visibility',
|
||||||
|
'private' => 'Private',
|
||||||
|
'default' => 'Default',
|
||||||
|
'all_day_handling' => 'All-day handling',
|
||||||
|
'timed' => 'Timed',
|
||||||
|
'all_day_coming' => 'Multi-day all-day UI coming soon',
|
||||||
|
'alerts' => 'Alerts',
|
||||||
|
'reminder' => 'Reminder',
|
||||||
|
'minutes_before' => 'minutes before',
|
||||||
|
'alerts_coming' => 'No alerts set. (Coming soon)',
|
||||||
|
'invitees' => 'Invitees',
|
||||||
|
'invitees_coming' => 'Invitees and RSVP tracking coming soon.',
|
||||||
|
'attachments' => 'Attachments',
|
||||||
|
'attachments_coming' => 'Attachment support coming soon.',
|
||||||
|
'notes' => 'Notes',
|
||||||
|
'no_description' => 'No description yet.',
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
68
lang/it/account.php
Normal file
68
lang/it/account.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Account Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Account, profile, and user settings language lines.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// addresses
|
||||||
|
'address' => [
|
||||||
|
'city' => 'Citta',
|
||||||
|
'country' => 'Paese',
|
||||||
|
'home' => 'Indirizzo di casa',
|
||||||
|
'label' => 'Etichetta indirizzo',
|
||||||
|
'line1' => 'Indirizzo riga 1',
|
||||||
|
'line2' => 'Indirizzo riga 2',
|
||||||
|
'state' => 'Provincia',
|
||||||
|
'work' => 'Indirizzo di lavoro',
|
||||||
|
'zip' => 'CAP',
|
||||||
|
],
|
||||||
|
'billing' => [
|
||||||
|
'home' => 'Usa il tuo indirizzo di casa per la fatturazione',
|
||||||
|
'work' => 'Usa il tuo indirizzo di lavoro per la fatturazione',
|
||||||
|
],
|
||||||
|
'delete' => 'Elimina account',
|
||||||
|
'delete-your' => 'Elimina il tuo account',
|
||||||
|
'delete-confirm' => 'Elimina davvero il mio account!',
|
||||||
|
'email' => 'Email',
|
||||||
|
'email_address' => 'Indirizzo email',
|
||||||
|
'first_name' => 'Nome',
|
||||||
|
'last_name' => 'Cognome',
|
||||||
|
'phone' => 'Numero di telefono',
|
||||||
|
'settings' => [
|
||||||
|
'addresses' => [
|
||||||
|
'title' => 'Indirizzi',
|
||||||
|
'subtitle' => 'Gestisci i tuoi indirizzi di casa e lavoro e scegli quale usare per la fatturazione.',
|
||||||
|
],
|
||||||
|
'delete' => [
|
||||||
|
'title' => 'Qui ci sono draghi',
|
||||||
|
'subtitle' => 'Elimina il tuo account e rimuovi tutte le informazioni dal nostro database. Non puo essere annullato, quindi consigliamo di esportare i tuoi dati prima e migrare a un nuovo provider.',
|
||||||
|
'explanation' => 'Nota: non e come altre app che "eliminano" i dati—non stiamo impostando <code>is_deleted = 1</code>, li stiamo rimuovendo dal nostro database.',
|
||||||
|
],
|
||||||
|
'delete-confirm' => [
|
||||||
|
'title' => 'Conferma eliminazione account',
|
||||||
|
'subtitle' => 'Inserisci la tua password e conferma che vuoi eliminare definitivamente il tuo account.',
|
||||||
|
],
|
||||||
|
'information' => [
|
||||||
|
'title' => 'Informazioni personali',
|
||||||
|
'subtitle' => 'Il tuo nome, email e altri dettagli principali del account.',
|
||||||
|
],
|
||||||
|
'locale' => [
|
||||||
|
'title' => 'Preferenze locali',
|
||||||
|
'subtitle' => 'Posizione, fuso orario e altre preferenze regionali per calendari ed eventi.'
|
||||||
|
],
|
||||||
|
'password' => [
|
||||||
|
'title' => 'Password',
|
||||||
|
'subtitle' => 'Assicurati che il tuo account usi una password lunga e casuale per restare sicuro. Consigliamo anche un password manager!',
|
||||||
|
],
|
||||||
|
'title' => 'Impostazioni account',
|
||||||
|
],
|
||||||
|
'title' => 'Account',
|
||||||
|
|
||||||
|
];
|
||||||
20
lang/it/auth.php
Normal file
20
lang/it/auth.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used during authentication for various
|
||||||
|
| messages that we need to display to the user. You are free to modify
|
||||||
|
| these language lines according to your application's requirements.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'failed' => 'Queste credenziali non corrispondono ai nostri record.',
|
||||||
|
'password' => 'La password fornita non e corretta.',
|
||||||
|
'throttle' => 'Troppi tentativi di accesso. Riprova tra :seconds secondi.',
|
||||||
|
|
||||||
|
];
|
||||||
80
lang/it/calendar.php
Normal file
80
lang/it/calendar.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Calendar Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used throughout the calendar app,
|
||||||
|
| including calendar settings and events.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'color' => 'Colore',
|
||||||
|
'create' => 'Crea calendario',
|
||||||
|
'description' => 'Descrizione',
|
||||||
|
'ics' => [
|
||||||
|
'url' => 'URL ICS',
|
||||||
|
'url_help' => 'Non puoi modificare un URL di calendario pubblico. Se devi fare una modifica, annulla l iscrizione e aggiungilo di nuovo.',
|
||||||
|
],
|
||||||
|
'mine' => 'I miei calendari',
|
||||||
|
'name' => 'Nome calendario',
|
||||||
|
'settings' => [
|
||||||
|
'calendar' => [
|
||||||
|
'title' => 'Impostazioni calendario',
|
||||||
|
'subtitle' => 'Dettagli e impostazioni per <strong>:calendar</strong>.'
|
||||||
|
],
|
||||||
|
'create' => [
|
||||||
|
'title' => 'Crea un calendario',
|
||||||
|
'subtitle' => 'Crea un nuovo calendario locale.',
|
||||||
|
],
|
||||||
|
'display' => [
|
||||||
|
'title' => 'Preferenze di visualizzazione',
|
||||||
|
'subtitle' => 'Regola aspetto e comportamento dei tuoi calendari.'
|
||||||
|
],
|
||||||
|
'language_region' => [
|
||||||
|
'title' => 'Lingua e regione',
|
||||||
|
'subtitle' => 'Scegli la lingua predefinita, la regione e le preferenze di formattazione. Queste influenzano come date e orari sono mostrati nei calendari e negli eventi.',
|
||||||
|
],
|
||||||
|
'my_calendars' => 'Impostazioni per i miei calendari',
|
||||||
|
'subscribe' => [
|
||||||
|
'title' => 'Iscriviti a un calendario',
|
||||||
|
'subtitle' => 'Aggiungi un calendario `.ics` da un altro servizio',
|
||||||
|
],
|
||||||
|
'saved' => 'Le impostazioni del calendario sono state salvate!',
|
||||||
|
'title' => 'Impostazioni calendario',
|
||||||
|
],
|
||||||
|
'timezone_help' => 'Puoi sovrascrivere il tuo fuso orario predefinito qui.',
|
||||||
|
'toggle_sidebar' => 'Mostra o nascondi la barra laterale del calendario',
|
||||||
|
'event' => [
|
||||||
|
'when' => 'Quando',
|
||||||
|
'all_day' => 'Tutto il giorno',
|
||||||
|
'location' => 'Luogo',
|
||||||
|
'map_coming' => 'Anteprima mappa in arrivo.',
|
||||||
|
'no_location' => 'Nessun luogo impostato.',
|
||||||
|
'details' => 'Dettagli',
|
||||||
|
'repeats' => 'Ripete',
|
||||||
|
'does_not_repeat' => 'Non si ripete',
|
||||||
|
'category' => 'Categoria',
|
||||||
|
'none' => 'Nessuno',
|
||||||
|
'visibility' => 'Visibilita',
|
||||||
|
'private' => 'Privato',
|
||||||
|
'default' => 'Predefinito',
|
||||||
|
'all_day_handling' => 'Gestione giornata intera',
|
||||||
|
'timed' => 'Con orario',
|
||||||
|
'all_day_coming' => 'UI giornate intere multi-giorno in arrivo',
|
||||||
|
'alerts' => 'Avvisi',
|
||||||
|
'reminder' => 'Promemoria',
|
||||||
|
'minutes_before' => 'minuti prima',
|
||||||
|
'alerts_coming' => 'Nessun avviso impostato. (In arrivo)',
|
||||||
|
'invitees' => 'Invitati',
|
||||||
|
'invitees_coming' => 'Invitati e RSVP in arrivo.',
|
||||||
|
'attachments' => 'Allegati',
|
||||||
|
'attachments_coming' => 'Supporto allegati in arrivo.',
|
||||||
|
'notes' => 'Note',
|
||||||
|
'no_description' => 'Nessuna descrizione.',
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
@ -2,10 +2,41 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Common words and phrases
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Generic words used throughout the app in more than one location.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'address' => 'Indirizzo',
|
||||||
|
'addresses' => 'Indirizzi',
|
||||||
'calendar' => 'Calendario',
|
'calendar' => 'Calendario',
|
||||||
'calendars' => 'Calendari',
|
'calendars' => 'Calendari',
|
||||||
|
'cancel' => 'Annulla',
|
||||||
|
'cancel_back' => 'Annulla e torna indietro',
|
||||||
|
'cancel_funny' => 'Portami via',
|
||||||
|
'date' => 'Data',
|
||||||
|
'date_select' => 'Seleziona una data',
|
||||||
|
'date_format' => 'Formato data',
|
||||||
|
'date_format_select' => 'Seleziona un formato data',
|
||||||
'event' => 'Evento',
|
'event' => 'Evento',
|
||||||
'events' => 'Eventi',
|
'events' => 'Eventi',
|
||||||
|
'language' => 'Lingua',
|
||||||
|
'language_select' => 'Seleziona una lingua',
|
||||||
|
'password' => 'Password',
|
||||||
|
'region' => 'Regione',
|
||||||
|
'region_select' => 'Seleziona una regione',
|
||||||
|
'save_changes' => 'Salva modifiche',
|
||||||
'settings' => 'Impostazioni',
|
'settings' => 'Impostazioni',
|
||||||
|
'time' => 'Ora',
|
||||||
|
'time_select' => 'Seleziona un orario',
|
||||||
|
'time_format' => 'Formato ora',
|
||||||
|
'time_format_select' => 'Seleziona un formato ora',
|
||||||
|
'timezone' => 'Fuso orario',
|
||||||
|
'timezone_default' => 'Fuso orario predefinito',
|
||||||
|
'timezone_select' => 'Seleziona un fuso orario',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
19
lang/it/pagination.php
Normal file
19
lang/it/pagination.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pagination Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used by the paginator library to build
|
||||||
|
| the simple pagination links. You are free to change them to anything
|
||||||
|
| you want to customize your views to better match your application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'previous' => '« Precedente',
|
||||||
|
'next' => 'Successivo »',
|
||||||
|
|
||||||
|
];
|
||||||
22
lang/it/passwords.php
Normal file
22
lang/it/passwords.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Password Reset Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are the default lines which match reasons
|
||||||
|
| that are given by the password broker for a password update attempt
|
||||||
|
| outcome such as failure due to an invalid password / reset token.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'reset' => 'La tua password e stata reimpostata.',
|
||||||
|
'sent' => 'Ti abbiamo inviato via email il link per reimpostare la password.',
|
||||||
|
'throttled' => 'Attendi prima di riprovare.',
|
||||||
|
'token' => 'Questo token di reimpostazione password non e valido.',
|
||||||
|
'user' => 'Non troviamo un utente con questo indirizzo email.',
|
||||||
|
|
||||||
|
];
|
||||||
198
lang/it/validation.php
Normal file
198
lang/it/validation.php
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Validation Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines contain the default error messages used by
|
||||||
|
| the validator class. Some of these rules have multiple versions such
|
||||||
|
| as the size rules. Feel free to tweak each of these messages here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'accepted' => 'Il campo :attribute deve essere accettato.',
|
||||||
|
'accepted_if' => 'Il campo :attribute deve essere accettato quando :other e :value.',
|
||||||
|
'active_url' => 'Il campo :attribute deve essere un URL valido.',
|
||||||
|
'after' => 'Il campo :attribute deve essere una data successiva a :date.',
|
||||||
|
'after_or_equal' => 'Il campo :attribute deve essere una data successiva o uguale a :date.',
|
||||||
|
'alpha' => 'Il campo :attribute deve contenere solo lettere.',
|
||||||
|
'alpha_dash' => 'Il campo :attribute deve contenere solo lettere, numeri, trattini e underscore.',
|
||||||
|
'alpha_num' => 'Il campo :attribute deve contenere solo lettere e numeri.',
|
||||||
|
'any_of' => 'Il campo :attribute non e valido.',
|
||||||
|
'array' => 'Il campo :attribute deve essere un array.',
|
||||||
|
'ascii' => 'Il campo :attribute deve contenere solo caratteri alfanumerici a singolo byte e simboli.',
|
||||||
|
'before' => 'Il campo :attribute deve essere una data precedente a :date.',
|
||||||
|
'before_or_equal' => 'Il campo :attribute deve essere una data precedente o uguale a :date.',
|
||||||
|
'between' => [
|
||||||
|
'array' => 'Il campo :attribute deve avere tra :min e :max elementi.',
|
||||||
|
'file' => 'Il campo :attribute deve essere tra :min e :max kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere tra :min e :max.',
|
||||||
|
'string' => 'Il campo :attribute deve essere tra :min e :max caratteri.',
|
||||||
|
],
|
||||||
|
'boolean' => 'Il campo :attribute deve essere vero o falso.',
|
||||||
|
'can' => 'Il campo :attribute contiene un valore non autorizzato.',
|
||||||
|
'confirmed' => 'La conferma del campo :attribute non corrisponde.',
|
||||||
|
'contains' => 'Il campo :attribute non contiene un valore richiesto.',
|
||||||
|
'current_password' => 'La password inserita non e corretta.',
|
||||||
|
'date' => 'Il campo :attribute deve essere una data valida.',
|
||||||
|
'date_equals' => 'Il campo :attribute deve essere una data uguale a :date.',
|
||||||
|
'date_format' => 'Il campo :attribute deve corrispondere al formato :format.',
|
||||||
|
'decimal' => 'Il campo :attribute deve avere :decimal decimali.',
|
||||||
|
'declined' => 'Il campo :attribute deve essere rifiutato.',
|
||||||
|
'declined_if' => 'Il campo :attribute deve essere rifiutato quando :other e :value.',
|
||||||
|
'different' => 'Il campo :attribute e :other devono essere diversi.',
|
||||||
|
'digits' => 'Il campo :attribute deve essere di :digits cifre.',
|
||||||
|
'digits_between' => 'Il campo :attribute deve essere tra :min e :max cifre.',
|
||||||
|
'dimensions' => 'Il campo :attribute ha dimensioni immagine non valide.',
|
||||||
|
'distinct' => 'Il campo :attribute ha un valore duplicato.',
|
||||||
|
'doesnt_end_with' => 'Il campo :attribute non deve terminare con uno dei seguenti: :values.',
|
||||||
|
'doesnt_start_with' => 'Il campo :attribute non deve iniziare con uno dei seguenti: :values.',
|
||||||
|
'email' => 'Il campo :attribute deve essere un indirizzo email valido.',
|
||||||
|
'ends_with' => 'Il campo :attribute deve terminare con uno dei seguenti: :values.',
|
||||||
|
'enum' => 'Il valore selezionato per :attribute non e valido.',
|
||||||
|
'exists' => 'Il valore selezionato per :attribute non e valido.',
|
||||||
|
'extensions' => 'Il campo :attribute deve avere una delle seguenti estensioni: :values.',
|
||||||
|
'file' => 'Il campo :attribute deve essere un file.',
|
||||||
|
'filled' => 'Il campo :attribute deve avere un valore.',
|
||||||
|
'gt' => [
|
||||||
|
'array' => 'Il campo :attribute deve avere piu di :value elementi.',
|
||||||
|
'file' => 'Il campo :attribute deve essere maggiore di :value kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere maggiore di :value.',
|
||||||
|
'string' => 'Il campo :attribute deve essere maggiore di :value caratteri.',
|
||||||
|
],
|
||||||
|
'gte' => [
|
||||||
|
'array' => 'Il campo :attribute deve avere :value elementi o piu.',
|
||||||
|
'file' => 'Il campo :attribute deve essere maggiore o uguale a :value kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere maggiore o uguale a :value.',
|
||||||
|
'string' => 'Il campo :attribute deve essere maggiore o uguale a :value caratteri.',
|
||||||
|
],
|
||||||
|
'hex_color' => 'Il campo :attribute deve essere un colore esadecimale valido.',
|
||||||
|
'image' => 'Il campo :attribute deve essere una immagine.',
|
||||||
|
'in' => 'Il valore selezionato per :attribute non e valido.',
|
||||||
|
'in_array' => 'Il campo :attribute deve esistere in :other.',
|
||||||
|
'in_array_keys' => 'Il campo :attribute deve contenere almeno una delle seguenti chiavi: :values.',
|
||||||
|
'integer' => 'Il campo :attribute deve essere un numero intero.',
|
||||||
|
'ip' => 'Il campo :attribute deve essere un indirizzo IP valido.',
|
||||||
|
'ipv4' => 'Il campo :attribute deve essere un indirizzo IPv4 valido.',
|
||||||
|
'ipv6' => 'Il campo :attribute deve essere un indirizzo IPv6 valido.',
|
||||||
|
'json' => 'Il campo :attribute deve essere una stringa JSON valida.',
|
||||||
|
'list' => 'Il campo :attribute deve essere una lista.',
|
||||||
|
'lowercase' => 'Il campo :attribute deve essere in minuscolo.',
|
||||||
|
'lt' => [
|
||||||
|
'array' => 'Il campo :attribute deve avere meno di :value elementi.',
|
||||||
|
'file' => 'Il campo :attribute deve essere minore di :value kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere minore di :value.',
|
||||||
|
'string' => 'Il campo :attribute deve essere minore di :value caratteri.',
|
||||||
|
],
|
||||||
|
'lte' => [
|
||||||
|
'array' => 'Il campo :attribute non deve avere piu di :value elementi.',
|
||||||
|
'file' => 'Il campo :attribute deve essere minore o uguale a :value kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere minore o uguale a :value.',
|
||||||
|
'string' => 'Il campo :attribute deve essere minore o uguale a :value caratteri.',
|
||||||
|
],
|
||||||
|
'mac_address' => 'Il campo :attribute deve essere un indirizzo MAC valido.',
|
||||||
|
'max' => [
|
||||||
|
'array' => 'Il campo :attribute non deve avere piu di :max elementi.',
|
||||||
|
'file' => 'Il campo :attribute non deve essere maggiore di :max kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute non deve essere maggiore di :max.',
|
||||||
|
'string' => 'Il campo :attribute non deve essere maggiore di :max caratteri.',
|
||||||
|
],
|
||||||
|
'max_digits' => 'Il campo :attribute non deve avere piu di :max cifre.',
|
||||||
|
'mimes' => 'Il campo :attribute deve essere un file di tipo: :values.',
|
||||||
|
'mimetypes' => 'Il campo :attribute deve essere un file di tipo: :values.',
|
||||||
|
'min' => [
|
||||||
|
'array' => 'Il campo :attribute deve avere almeno :min elementi.',
|
||||||
|
'file' => 'Il campo :attribute deve essere almeno :min kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere almeno :min.',
|
||||||
|
'string' => 'Il campo :attribute deve essere almeno :min caratteri.',
|
||||||
|
],
|
||||||
|
'min_digits' => 'Il campo :attribute deve avere almeno :min cifre.',
|
||||||
|
'missing' => 'Il campo :attribute deve essere assente.',
|
||||||
|
'missing_if' => 'Il campo :attribute deve essere assente quando :other e :value.',
|
||||||
|
'missing_unless' => 'Il campo :attribute deve essere assente a meno che :other sia :value.',
|
||||||
|
'missing_with' => 'Il campo :attribute deve essere assente quando :values e presente.',
|
||||||
|
'missing_with_all' => 'Il campo :attribute deve essere assente quando :values sono presenti.',
|
||||||
|
'multiple_of' => 'Il campo :attribute deve essere un multiplo di :value.',
|
||||||
|
'not_in' => 'Il valore selezionato per :attribute non e valido.',
|
||||||
|
'not_regex' => 'Il formato del campo :attribute non e valido.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere un numero.',
|
||||||
|
'password' => [
|
||||||
|
'letters' => 'Il campo :attribute deve contenere almeno una lettera.',
|
||||||
|
'mixed' => 'Il campo :attribute deve contenere almeno una lettera maiuscola e una minuscola.',
|
||||||
|
'numbers' => 'Il campo :attribute deve contenere almeno un numero.',
|
||||||
|
'symbols' => 'Il campo :attribute deve contenere almeno un simbolo.',
|
||||||
|
'uncompromised' => 'Il valore :attribute e apparso in una violazione di dati. Scegli un altro :attribute.',
|
||||||
|
],
|
||||||
|
'present' => 'Il campo :attribute deve essere presente.',
|
||||||
|
'present_if' => 'Il campo :attribute deve essere presente quando :other e :value.',
|
||||||
|
'present_unless' => 'Il campo :attribute deve essere presente a meno che :other sia :value.',
|
||||||
|
'present_with' => 'Il campo :attribute deve essere presente quando :values e presente.',
|
||||||
|
'present_with_all' => 'Il campo :attribute deve essere presente quando :values sono presenti.',
|
||||||
|
'prohibited' => 'Il campo :attribute e proibito.',
|
||||||
|
'prohibited_if' => 'Il campo :attribute e proibito quando :other e :value.',
|
||||||
|
'prohibited_if_accepted' => 'Il campo :attribute e proibito quando :other e accettato.',
|
||||||
|
'prohibited_if_declined' => 'Il campo :attribute e proibito quando :other e rifiutato.',
|
||||||
|
'prohibited_unless' => 'Il campo :attribute e proibito a meno che :other sia in :values.',
|
||||||
|
'prohibits' => 'Il campo :attribute impedisce la presenza di :other.',
|
||||||
|
'regex' => 'Il formato del campo :attribute non e valido.',
|
||||||
|
'required' => 'Il campo :attribute e obbligatorio.',
|
||||||
|
'required_array_keys' => 'Il campo :attribute deve contenere voci per: :values.',
|
||||||
|
'required_if' => 'Il campo :attribute e obbligatorio quando :other e :value.',
|
||||||
|
'required_if_accepted' => 'Il campo :attribute e obbligatorio quando :other e accettato.',
|
||||||
|
'required_if_declined' => 'Il campo :attribute e obbligatorio quando :other e rifiutato.',
|
||||||
|
'required_unless' => 'Il campo :attribute e obbligatorio a meno che :other sia in :values.',
|
||||||
|
'required_with' => 'Il campo :attribute e obbligatorio quando :values e presente.',
|
||||||
|
'required_with_all' => 'Il campo :attribute e obbligatorio quando :values sono presenti.',
|
||||||
|
'required_without' => 'Il campo :attribute e obbligatorio quando :values non e presente.',
|
||||||
|
'required_without_all' => 'Il campo :attribute e obbligatorio quando nessuno di :values e presente.',
|
||||||
|
'same' => 'Il campo :attribute deve corrispondere a :other.',
|
||||||
|
'size' => [
|
||||||
|
'array' => 'Il campo :attribute deve contenere :size elementi.',
|
||||||
|
'file' => 'Il campo :attribute deve essere di :size kilobyte.',
|
||||||
|
'numeric' => 'Il campo :attribute deve essere :size.',
|
||||||
|
'string' => 'Il campo :attribute deve essere di :size caratteri.',
|
||||||
|
],
|
||||||
|
'starts_with' => 'Il campo :attribute deve iniziare con uno dei seguenti: :values.',
|
||||||
|
'string' => 'Il campo :attribute deve essere una stringa.',
|
||||||
|
'timezone' => 'Il campo :attribute deve essere un fuso orario valido.',
|
||||||
|
'unique' => 'Il valore :attribute e gia stato preso.',
|
||||||
|
'uploaded' => 'Il campo :attribute non e riuscito a caricare.',
|
||||||
|
'uppercase' => 'Il campo :attribute deve essere in maiuscolo.',
|
||||||
|
'url' => 'Il campo :attribute deve essere un URL valido.',
|
||||||
|
'ulid' => 'Il campo :attribute deve essere un ULID valido.',
|
||||||
|
'uuid' => 'Il campo :attribute deve essere un UUID valido.',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Custom Validation Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify custom validation messages for attributes using the
|
||||||
|
| convention "attribute.rule" to name the lines. This makes it quick to
|
||||||
|
| specify a specific custom language line for a given attribute rule.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'custom' => [
|
||||||
|
'attribute-name' => [
|
||||||
|
'rule-name' => 'custom-message',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Custom Validation Attributes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used to swap our attribute placeholder
|
||||||
|
| with something more reader friendly such as "E-Mail Address" instead
|
||||||
|
| of "email". This simply helps us make our message more expressive.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'attributes' => [],
|
||||||
|
|
||||||
|
];
|
||||||
@ -188,9 +188,9 @@ main {
|
|||||||
container: content / inline-size;
|
container: content / inline-size;
|
||||||
|
|
||||||
/* main content title and actions */
|
/* main content title and actions */
|
||||||
> header {
|
header {
|
||||||
@apply flex flex-row items-center justify-between w-full;
|
@apply flex flex-row items-center justify-between w-full;
|
||||||
@apply bg-white sticky top-0 z-10;
|
@apply bg-white sticky top-0 z-20;
|
||||||
|
|
||||||
/* app hedar; if h1 exists it means there's no aside, so force the width from that */
|
/* app hedar; if h1 exists it means there's no aside, so force the width from that */
|
||||||
h1 {
|
h1 {
|
||||||
@ -210,7 +210,10 @@ main {
|
|||||||
|
|
||||||
/* header menu */
|
/* header menu */
|
||||||
menu {
|
menu {
|
||||||
@apply flex flex-row items-center justify-end gap-4;
|
@apply fixed right-0 top-2 flex flex-col bg-gray-100 gap-6 p-6 rounded-l-xl;
|
||||||
|
height: calc(100dvh - 0.5rem);
|
||||||
|
width: 33dvw;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +245,18 @@ main {
|
|||||||
|
|
||||||
/* section specific */
|
/* section specific */
|
||||||
&#calendar {
|
&#calendar {
|
||||||
/* */
|
header {
|
||||||
|
h2 {
|
||||||
|
.calendar-expand-toggle {
|
||||||
|
@apply ml-1 opacity-0 pointer-events-none transition-opacity duration-150;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .calendar-expand-toggle,
|
||||||
|
&:focus-within .calendar-expand-toggle {
|
||||||
|
@apply opacity-100 pointer-events-auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&#settings {
|
&#settings {
|
||||||
/* */
|
/* */
|
||||||
@ -250,6 +264,21 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* container sizing */
|
||||||
|
@container content (width >= 64rem)
|
||||||
|
{
|
||||||
|
main {
|
||||||
|
article {
|
||||||
|
header {
|
||||||
|
menu {
|
||||||
|
@apply relative top-auto right-auto h-auto w-auto rounded-none bg-transparent;
|
||||||
|
@apply flex flex-row items-center justify-end gap-4 p-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* app logo */
|
/* app logo */
|
||||||
.logo {
|
.logo {
|
||||||
@apply w-10 h-10 flex;
|
@apply w-10 h-10 flex;
|
||||||
|
|||||||
@ -158,7 +158,7 @@
|
|||||||
--event-fg: var(--color-primary);
|
--event-fg: var(--color-primary);
|
||||||
|
|
||||||
li.event {
|
li.event {
|
||||||
@apply flex rounded-md relative;
|
@apply flex rounded-md relative border border-white;
|
||||||
background-color: var(--event-bg);
|
background-color: var(--event-bg);
|
||||||
color: var(--event-fg);
|
color: var(--event-fg);
|
||||||
grid-row-start: var(--event-row);
|
grid-row-start: var(--event-row);
|
||||||
@ -166,22 +166,21 @@
|
|||||||
grid-column-start: var(--event-col);
|
grid-column-start: var(--event-col);
|
||||||
grid-column-end: calc(var(--event-col) + 1);
|
grid-column-end: calc(var(--event-col) + 1);
|
||||||
top: 0.6rem;
|
top: 0.6rem;
|
||||||
transition: translate 100ms ease-in;
|
|
||||||
|
|
||||||
> a {
|
a.event {
|
||||||
@apply flex flex-col grow px-3 py-2 gap-2px;
|
@apply flex flex-col grow px-3 py-2 gap-2px text-sm;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
@apply font-semibold leading-none break-all;
|
@apply font-semibold leading-none break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
> time {
|
> time {
|
||||||
@apply text-sm;
|
@apply text-xs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@apply -translate-y-2px;
|
animation: event-hover 125ms ease forwards;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,19 +215,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* step handling */
|
/* step handling */
|
||||||
.calendar.time[data-density="30"] {
|
.calendar.time[data-density="30"] { /* half-hourly */
|
||||||
--row-height: 2rem;
|
--row-height: 2rem;
|
||||||
|
|
||||||
ol.time li:nth-child(2n) {
|
ol.time li:nth-child(2n) {
|
||||||
visibility: hidden; /* preserves space + row alignment */
|
visibility: hidden; /* preserves space + row alignment */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.calendar.time[data-density="60"] {
|
.calendar.time[data-density="60"] { /* hourly */
|
||||||
--row-height: 1.25rem;
|
--row-height: 1.25rem;
|
||||||
|
|
||||||
ol.time li:not(:nth-child(4n + 1)) {
|
ol.time li:not(:nth-child(4n + 1)) {
|
||||||
visibility: hidden; /* preserves space + row alignment */
|
visibility: hidden; /* preserves space + row alignment */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.week {
|
||||||
|
ol.events {
|
||||||
|
li.event[data-span="1"] {
|
||||||
|
a.event > span,
|
||||||
|
a.event > time {
|
||||||
|
@apply text-xs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
li.event[data-span="1"],
|
||||||
|
li.event[data-span="2"] {
|
||||||
|
> a.event {
|
||||||
|
@apply flex-row items-center gap-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,7 +394,16 @@
|
|||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@keyframes event-hover {
|
||||||
|
from {
|
||||||
|
transform: translateY(0);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@keyframes header-slide {
|
@keyframes header-slide {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@ -3,26 +3,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialog {
|
dialog {
|
||||||
@apply grid fixed top-0 right-0 bottom-0 left-0 m-0 p-0 pointer-events-none;
|
@apply grid fixed inset-0 m-0 p-0 pointer-events-none;
|
||||||
@apply justify-items-center items-start bg-transparent opacity-0 invisible;
|
@apply place-items-center bg-transparent opacity-0 invisible;
|
||||||
@apply w-full h-full max-w-full max-h-full overflow-y-hidden;
|
@apply w-full h-full max-w-none max-h-none overflow-clip;
|
||||||
background-color: rgba(26, 26, 26, 0.75);
|
background-color: rgba(26, 26, 26, 0.75);
|
||||||
backdrop-filter: blur(0.25rem);
|
backdrop-filter: blur(0.25rem);
|
||||||
grid-template-rows: minmax(20dvh, 2rem) 1fr;
|
/*(grid-template-rows: minmax(20dvh, 2rem) 1fr; */
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
scrollbar-gutter: auto;
|
||||||
transition:
|
transition:
|
||||||
|
background-color 150ms cubic-bezier(0,0,.2,1),
|
||||||
opacity 150ms cubic-bezier(0,0,.2,1),
|
opacity 150ms cubic-bezier(0,0,.2,1),
|
||||||
visibility 150ms cubic-bezier(0,0,.2,1);
|
visibility 150ms cubic-bezier(0,0,.2,1);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
#modal {
|
#modal {
|
||||||
@apply relative rounded-lg bg-white border-gray-200 p-0;
|
@apply relative rounded-xl bg-white border-gray-200 p-0;
|
||||||
@apply flex flex-col items-start col-start-1 row-start-2 translate-y-4;
|
@apply flex flex-col items-start col-start-1 translate-y-4;
|
||||||
@apply overscroll-contain overflow-y-auto;
|
@apply overscroll-contain overflow-y-auto;
|
||||||
max-height: calc(100vh - 5em);
|
max-height: calc(100vh - 5em);
|
||||||
width: 91.666667%;
|
width: 91.666667%;
|
||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
transition: all 150ms cubic-bezier(0,0,.2,1);
|
transition: all 150ms cubic-bezier(0,0,.2,1);
|
||||||
box-shadow: #00000040 0 1.5rem 4rem -0.5rem;
|
box-shadow: 0 1.5rem 4rem -0.5rem rgba(0, 0, 0, 0.4);
|
||||||
|
|
||||||
> .close-modal {
|
> .close-modal {
|
||||||
@apply block absolute top-4 right-4;
|
@apply block absolute top-4 right-4;
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
import './bootstrap';
|
import './bootstrap';
|
||||||
import htmx from 'htmx.org';
|
import htmx from 'htmx.org';
|
||||||
|
|
||||||
|
const SELECTORS = {
|
||||||
|
calendarToggle: '.calendar-toggle',
|
||||||
|
calendarViewForm: '#calendar-view',
|
||||||
|
calendarExpandToggle: '[data-calendar-expand]',
|
||||||
|
colorPicker: '[data-colorpicker]',
|
||||||
|
colorPickerColor: '[data-colorpicker-color]',
|
||||||
|
colorPickerHex: '[data-colorpicker-hex]',
|
||||||
|
colorPickerRandom: '[data-colorpicker-random]',
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* htmx/global
|
* htmx/global
|
||||||
*/
|
*/
|
||||||
@ -19,34 +29,43 @@ document.addEventListener('htmx:configRequest', (evt) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* calendar toggle
|
* calendar ui
|
||||||
* progressive enhancement on html form with no js
|
* progressive enhancement on html form with no js
|
||||||
*/
|
*/
|
||||||
document.addEventListener('change', event => {
|
document.addEventListener('change', (event) => {
|
||||||
const checkbox = event.target;
|
const target = event.target;
|
||||||
|
|
||||||
// ignore anything that isn’t one of our checkboxes
|
if (target?.matches(SELECTORS.calendarToggle)) {
|
||||||
if (!checkbox.matches('.calendar-toggle')) return;
|
const slug = target.value;
|
||||||
|
const show = target.checked;
|
||||||
|
|
||||||
const slug = checkbox.value;
|
document
|
||||||
const show = checkbox.checked;
|
.querySelectorAll(`[data-calendar="${slug}"]`)
|
||||||
|
.forEach(el => el.classList.toggle('hidden', !show));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// toggle .hidden on every matching event element
|
const form = target?.form;
|
||||||
document
|
if (!form || form.id !== 'calendar-view') return;
|
||||||
.querySelectorAll(`[data-calendar="${slug}"]`)
|
if (target.name !== 'view') return;
|
||||||
.forEach(el => el.classList.toggle('hidden', !show));
|
|
||||||
|
form.requestSubmit();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* calendar view picker
|
* calendar sidebar expand toggle
|
||||||
* progressive enhancement on html form with no js
|
|
||||||
*/
|
*/
|
||||||
document.addEventListener('change', (e) => {
|
document.addEventListener('click', (event) => {
|
||||||
const form = e.target?.form;
|
const toggle = event.target.closest(SELECTORS.calendarExpandToggle);
|
||||||
if (!form || form.id !== 'calendar-view') return;
|
if (!toggle) return;
|
||||||
if (e.target.name !== 'view') return;
|
|
||||||
|
|
||||||
form.requestSubmit();
|
event.preventDefault();
|
||||||
|
|
||||||
|
const main = toggle.closest('main');
|
||||||
|
if (!main) return;
|
||||||
|
|
||||||
|
const isExpanded = main.classList.toggle('expanded');
|
||||||
|
toggle.setAttribute('aria-pressed', isExpanded ? 'true' : 'false');
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,9 +90,9 @@ function initColorPickers(root = document) {
|
|||||||
if (el.__colorpickerWired) return;
|
if (el.__colorpickerWired) return;
|
||||||
el.__colorpickerWired = true;
|
el.__colorpickerWired = true;
|
||||||
|
|
||||||
const color = el.querySelector('[data-colorpicker-color]');
|
const color = el.querySelector(SELECTORS.colorPickerColor);
|
||||||
const hex = el.querySelector('[data-colorpicker-hex]');
|
const hex = el.querySelector(SELECTORS.colorPickerHex);
|
||||||
const btn = el.querySelector('[data-colorpicker-random]');
|
const btn = el.querySelector(SELECTORS.colorPickerRandom);
|
||||||
|
|
||||||
if (!color || !hex) return;
|
if (!color || !hex) return;
|
||||||
|
|
||||||
@ -137,11 +156,15 @@ function initColorPickers(root = document) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
root.querySelectorAll('[data-colorpicker]').forEach(wire);
|
root.querySelectorAll(SELECTORS.colorPicker).forEach(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initUI() {
|
||||||
|
initColorPickers();
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial bind
|
// initial bind
|
||||||
document.addEventListener('DOMContentLoaded', () => initColorPickers());
|
document.addEventListener('DOMContentLoaded', initUI);
|
||||||
|
|
||||||
// rebind in htmx for swapped content
|
// rebind in htmx for swapped content
|
||||||
document.addEventListener('htmx:afterSwap', (e) => {
|
document.addEventListener('htmx:afterSwap', (e) => {
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
<x-app-layout>
|
|
||||||
<x-slot name="header">
|
|
||||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
|
||||||
{{ __('Profile') }}
|
|
||||||
</h2>
|
|
||||||
</x-slot>
|
|
||||||
|
|
||||||
<div class="py-12">
|
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
|
||||||
<div class="p-4 sm:p-8 bg-white shadow-sm sm:rounded-lg">
|
|
||||||
<div class="max-w-xl">
|
|
||||||
@include('account.partials.update-profile-information-form')
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-4 sm:p-8 bg-white shadow-sm sm:rounded-lg">
|
|
||||||
<div class="max-w-xl">
|
|
||||||
@include('account.partials.addresses-form', [
|
|
||||||
'home' => $home ?? null,
|
|
||||||
'billing' => $billing ?? null,
|
|
||||||
])
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-4 sm:p-8 bg-white shadow-sm sm:rounded-lg">
|
|
||||||
<div class="max-w-xl">
|
|
||||||
@include('account.partials.update-password-form')
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-4 sm:p-8 bg-white shadow-sm sm:rounded-lg">
|
|
||||||
<div class="max-w-xl">
|
|
||||||
@include('account.partials.delete-user-form')
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</x-app-layout>
|
|
||||||
@ -85,6 +85,14 @@
|
|||||||
@if(!empty($header['span']))
|
@if(!empty($header['span']))
|
||||||
<span>{{ $header['span'] }}</span>
|
<span>{{ $header['span'] }}</span>
|
||||||
@endif
|
@endif
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button button--icon button--sm calendar-expand-toggle"
|
||||||
|
data-calendar-expand
|
||||||
|
aria-label="{{ __('calendar.toggle_sidebar') }}"
|
||||||
|
>
|
||||||
|
<x-icon-chevron-left />
|
||||||
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<menu>
|
<menu>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
data-calendar-id="{{ $event['calendar_slug'] }}"
|
data-calendar-id="{{ $event['calendar_slug'] }}"
|
||||||
data-start="{{ $event['start_ui'] }}"
|
data-start="{{ $event['start_ui'] }}"
|
||||||
data-duration="{{ $event['duration'] }}"
|
data-duration="{{ $event['duration'] }}"
|
||||||
|
data-span="{{ $event['row_span'] }}"
|
||||||
style="
|
style="
|
||||||
--event-row: {{ $event['start_row'] }};
|
--event-row: {{ $event['start_row'] }};
|
||||||
--event-end: {{ $event['end_row'] }};
|
--event-end: {{ $event['end_row'] }};
|
||||||
|
|||||||
@ -1,26 +1,147 @@
|
|||||||
|
@php
|
||||||
|
$meta = $event->meta;
|
||||||
|
$title = $meta->title ?? '(no title)';
|
||||||
|
$allDay = (bool) ($meta->all_day ?? false);
|
||||||
|
$calendarName = $calendar->displayname ?? __('common.calendar');
|
||||||
|
$calendarColor = $calendar->meta_color ?? $calendar->calendarcolor ?? default_calendar_color();
|
||||||
|
$rrule = $meta?->extra['rrule'] ?? null;
|
||||||
|
$tzid = $meta?->extra['tzid'] ?? $tz;
|
||||||
|
$locationLabel = $meta?->location_label ?? '';
|
||||||
|
$hasLocation = trim((string) $locationLabel) !== '';
|
||||||
|
$venue = $meta?->venue;
|
||||||
|
$addressLine1 = $venue?->street;
|
||||||
|
$addressLine2 = trim(implode(', ', array_filter([
|
||||||
|
$venue?->city,
|
||||||
|
$venue?->state,
|
||||||
|
$venue?->postal,
|
||||||
|
])));
|
||||||
|
$addressLine3 = $venue?->country;
|
||||||
|
@endphp
|
||||||
|
|
||||||
<x-modal.content>
|
<x-modal.content>
|
||||||
<x-modal.title>
|
<x-modal.title>
|
||||||
{{ $event->meta->title ?? '(no title)' }}
|
<div class="flex items-center gap-3">
|
||||||
|
<span class="inline-block h-3 w-3 rounded-full" style="background: {{ $calendarColor }};"></span>
|
||||||
|
<span>{{ $title }}</span>
|
||||||
|
</div>
|
||||||
</x-modal.title>
|
</x-modal.title>
|
||||||
<x-modal.body>
|
<x-modal.body>
|
||||||
<p class="text-gray-700">
|
<div class="flex flex-col gap-6">
|
||||||
{{ $start->format('l, F j, Y · g:i A') }}
|
<section class="space-y-1">
|
||||||
@unless ($start->equalTo($end))
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.when') }}</p>
|
||||||
–
|
@if ($allDay)
|
||||||
{{ $end->isSameDay($start)
|
<p class="text-lg text-gray-900">
|
||||||
? $end->format('g:i A')
|
{{ $start->format('l, F j, Y') }}
|
||||||
: $end->format('l, F j, Y · g:i A') }}
|
@unless ($start->isSameDay($end))
|
||||||
@endunless
|
–
|
||||||
</p>
|
{{ $end->format('l, F j, Y') }}
|
||||||
|
@endunless
|
||||||
|
<span class="text-sm text-gray-500">({{ __('calendar.event.all_day') }})</span>
|
||||||
|
</p>
|
||||||
|
@else
|
||||||
|
<p class="text-lg text-gray-900">
|
||||||
|
{{ $start->format('l, F j, Y · g:i A') }}
|
||||||
|
@unless ($start->equalTo($end))
|
||||||
|
–
|
||||||
|
{{ $end->isSameDay($start)
|
||||||
|
? $end->format('g:i A')
|
||||||
|
: $end->format('l, F j, Y · g:i A') }}
|
||||||
|
@endunless
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
<p class="text-sm text-gray-500">{{ __('common.timezone') }}: {{ $tzid }}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
@if ($event->meta->location)
|
<section class="space-y-1">
|
||||||
<p><strong>Where:</strong> {{ $event->meta->location_label }}</p>
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('common.calendar') }}</p>
|
||||||
@endif
|
<p class="text-gray-900">{{ $calendarName }}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
@if ($event->meta->description)
|
<section class="space-y-2">
|
||||||
<p>
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.location') }}</p>
|
||||||
{!! Str::markdown(nl2br(e($event->meta->description))) !!}
|
@if ($hasLocation)
|
||||||
</p>
|
<p class="text-gray-900">{{ $locationLabel }}</p>
|
||||||
@endif
|
@if ($addressLine1 || $addressLine2 || $addressLine3)
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
@if ($addressLine1)
|
||||||
|
<div>{{ $addressLine1 }}</div>
|
||||||
|
@endif
|
||||||
|
@if ($addressLine2)
|
||||||
|
<div>{{ $addressLine2 }}</div>
|
||||||
|
@endif
|
||||||
|
@if ($addressLine3)
|
||||||
|
<div>{{ $addressLine3 }}</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div class="mt-2 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4 text-sm text-gray-500">
|
||||||
|
{{ __('calendar.event.map_coming') }}
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<p class="text-sm text-gray-500">{{ __('calendar.event.no_location') }}</p>
|
||||||
|
@endif
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="space-y-2">
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.details') }}</p>
|
||||||
|
<div class="grid grid-cols-1 gap-3 text-sm text-gray-700">
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-500">{{ __('calendar.event.repeats') }}:</span>
|
||||||
|
@if ($rrule)
|
||||||
|
<span class="ml-1 font-mono text-gray-800">{{ $rrule }}</span>
|
||||||
|
@else
|
||||||
|
<span class="ml-1 text-gray-500">{{ __('calendar.event.does_not_repeat') }}</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-500">{{ __('calendar.event.category') }}:</span>
|
||||||
|
<span class="ml-1">{{ $meta->category ?? __('calendar.event.none') }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-500">{{ __('calendar.event.visibility') }}:</span>
|
||||||
|
<span class="ml-1">{{ ($meta->is_private ?? false) ? __('calendar.event.private') : __('calendar.event.default') }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="text-gray-500">{{ __('calendar.event.all_day_handling') }}:</span>
|
||||||
|
<span class="ml-1">
|
||||||
|
{{ $allDay ? __('calendar.event.all_day') : __('calendar.event.timed') }}
|
||||||
|
<span class="text-gray-400">· {{ __('calendar.event.all_day_coming') }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="space-y-2">
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.alerts') }}</p>
|
||||||
|
@if (!is_null($meta->reminder_minutes))
|
||||||
|
<p class="text-sm text-gray-700">
|
||||||
|
{{ __('calendar.event.reminder') }}: {{ $meta->reminder_minutes }} {{ __('calendar.event.minutes_before') }}
|
||||||
|
</p>
|
||||||
|
@else
|
||||||
|
<p class="text-sm text-gray-500">{{ __('calendar.event.alerts_coming') }}</p>
|
||||||
|
@endif
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="space-y-2">
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.invitees') }}</p>
|
||||||
|
<p class="text-sm text-gray-500">{{ __('calendar.event.invitees_coming') }}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="space-y-2">
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.attachments') }}</p>
|
||||||
|
<p class="text-sm text-gray-500">{{ __('calendar.event.attachments_coming') }}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="space-y-2">
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-400">{{ __('calendar.event.notes') }}</p>
|
||||||
|
@if ($meta->description)
|
||||||
|
<div class="prose prose-sm max-w-none text-gray-800">
|
||||||
|
{!! Str::markdown(nl2br(e($meta->description))) !!}
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<p class="text-sm text-gray-500">{{ __('calendar.event.no_description') }}</p>
|
||||||
|
@endif
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</x-modal.body>
|
</x-modal.body>
|
||||||
</x-modal.content>
|
</x-modal.content>
|
||||||
|
|||||||
@ -28,9 +28,6 @@
|
|||||||
|
|
||||||
<!-- bottom -->
|
<!-- bottom -->
|
||||||
<section class="bottom">
|
<section class="bottom">
|
||||||
<x-button.icon type="anchor" :href="route('settings')">
|
|
||||||
<x-icon-settings class="w-7 h-7" />
|
|
||||||
</x-button.icon>
|
|
||||||
<x-button.icon type="anchor" :href="route('account.index')">
|
<x-button.icon type="anchor" :href="route('account.index')">
|
||||||
<x-icon-user-circle class="w-7 h-7" />
|
<x-icon-user-circle class="w-7 h-7" />
|
||||||
</x-button.icon>
|
</x-button.icon>
|
||||||
|
|||||||
@ -2,32 +2,35 @@
|
|||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
test('profile page is displayed', function () {
|
test('account info page is displayed', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->get('/profile');
|
->get('/account/info');
|
||||||
|
|
||||||
$response->assertOk();
|
$response->assertOk();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('profile information can be updated', function () {
|
test('account information can be updated', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->patch('/profile', [
|
->patch('/account/info', [
|
||||||
'name' => 'Test User',
|
'firstname' => 'Test',
|
||||||
|
'lastname' => 'User',
|
||||||
'email' => 'test@example.com',
|
'email' => 'test@example.com',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->assertSessionHasNoErrors()
|
->assertSessionHasNoErrors()
|
||||||
->assertRedirect('/profile');
|
->assertRedirect('/account/info');
|
||||||
|
|
||||||
$user->refresh();
|
$user->refresh();
|
||||||
|
|
||||||
|
$this->assertSame('Test', $user->firstname);
|
||||||
|
$this->assertSame('User', $user->lastname);
|
||||||
$this->assertSame('Test User', $user->name);
|
$this->assertSame('Test User', $user->name);
|
||||||
$this->assertSame('test@example.com', $user->email);
|
$this->assertSame('test@example.com', $user->email);
|
||||||
$this->assertNull($user->email_verified_at);
|
$this->assertNull($user->email_verified_at);
|
||||||
@ -38,14 +41,15 @@ test('email verification status is unchanged when the email address is unchanged
|
|||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->patch('/profile', [
|
->patch('/account/info', [
|
||||||
'name' => 'Test User',
|
'firstname' => 'Test',
|
||||||
|
'lastname' => 'User',
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->assertSessionHasNoErrors()
|
->assertSessionHasNoErrors()
|
||||||
->assertRedirect('/profile');
|
->assertRedirect('/account/info');
|
||||||
|
|
||||||
$this->assertNotNull($user->refresh()->email_verified_at);
|
$this->assertNotNull($user->refresh()->email_verified_at);
|
||||||
});
|
});
|
||||||
@ -55,13 +59,13 @@ test('user can delete their account', function () {
|
|||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->delete('/profile', [
|
->delete('/account', [
|
||||||
'password' => 'password',
|
'password' => 'password',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->assertSessionHasNoErrors()
|
->assertSessionHasNoErrors()
|
||||||
->assertRedirect('/');
|
->assertRedirect('/dashboard');
|
||||||
|
|
||||||
$this->assertGuest();
|
$this->assertGuest();
|
||||||
$this->assertNull($user->fresh());
|
$this->assertNull($user->fresh());
|
||||||
@ -72,14 +76,14 @@ test('correct password must be provided to delete account', function () {
|
|||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->from('/profile')
|
->from('/account/delete/confirm')
|
||||||
->delete('/profile', [
|
->delete('/account', [
|
||||||
'password' => 'wrong-password',
|
'password' => 'wrong-password',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->assertSessionHasErrorsIn('userDeletion', 'password')
|
->assertSessionHasErrorsIn('userDeletion', 'password')
|
||||||
->assertRedirect('/profile');
|
->assertRedirect('/account/delete/confirm');
|
||||||
|
|
||||||
$this->assertNotNull($user->fresh());
|
$this->assertNotNull($user->fresh());
|
||||||
});
|
});
|
||||||
@ -8,7 +8,7 @@ test('password can be updated', function () {
|
|||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->from('/profile')
|
->from('/account/password')
|
||||||
->put('/password', [
|
->put('/password', [
|
||||||
'current_password' => 'password',
|
'current_password' => 'password',
|
||||||
'password' => 'new-password',
|
'password' => 'new-password',
|
||||||
@ -17,7 +17,7 @@ test('password can be updated', function () {
|
|||||||
|
|
||||||
$response
|
$response
|
||||||
->assertSessionHasNoErrors()
|
->assertSessionHasNoErrors()
|
||||||
->assertRedirect('/profile');
|
->assertRedirect('/account/password');
|
||||||
|
|
||||||
$this->assertTrue(Hash::check('new-password', $user->refresh()->password));
|
$this->assertTrue(Hash::check('new-password', $user->refresh()->password));
|
||||||
});
|
});
|
||||||
@ -27,7 +27,7 @@ test('correct password must be provided to update password', function () {
|
|||||||
|
|
||||||
$response = $this
|
$response = $this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->from('/profile')
|
->from('/account/password')
|
||||||
->put('/password', [
|
->put('/password', [
|
||||||
'current_password' => 'wrong-password',
|
'current_password' => 'wrong-password',
|
||||||
'password' => 'new-password',
|
'password' => 'new-password',
|
||||||
@ -36,5 +36,5 @@ test('correct password must be provided to update password', function () {
|
|||||||
|
|
||||||
$response
|
$response
|
||||||
->assertSessionHasErrorsIn('updatePassword', 'current_password')
|
->assertSessionHasErrorsIn('updatePassword', 'current_password')
|
||||||
->assertRedirect('/profile');
|
->assertRedirect('/account/password');
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user