updated so many things, my bad
This commit is contained in:
parent
938e1f8c96
commit
6cf7a5e3d1
@ -37,7 +37,7 @@ class ResetPassword extends Command
|
|||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$id = $this->option('id');
|
$id = $this->option('id');
|
||||||
$email = $this->option('email');
|
$email = $this->option('email');
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('sessions', function (Blueprint $table) {
|
||||||
|
$table->string('id')->primary();
|
||||||
|
$table->string('user_id', 64);
|
||||||
|
$table->string('ip_address', 45)->nullable();
|
||||||
|
$table->text('user_agent')->nullable()->index();
|
||||||
|
$table->text('payload');
|
||||||
|
$table->integer('last_activity')->index();
|
||||||
|
$table->timestamp('vr_last_activity_at')->virtualAs('FROM_UNIXTIME(last_activity)');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('sessions');
|
||||||
|
}
|
||||||
|
};
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
@import 'components/buttons.css';
|
@import 'components/buttons.css';
|
||||||
@import 'components/cards.css';
|
@import 'components/cards.css';
|
||||||
|
@import 'components/forms.css';
|
||||||
@import 'components/tables.css';
|
@import 'components/tables.css';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -17,6 +18,19 @@ body {
|
|||||||
scrollbar-gutter: stable both-edges;
|
scrollbar-gutter: stable both-edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
transition-duration: 0.05s;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
transition-property: background, color;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, a {
|
||||||
|
transition-property: all;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1023px) {
|
@media (max-width: 1023px) {
|
||||||
.grid-container {
|
.grid-container {
|
||||||
@apply grid-cols-1;
|
@apply grid-cols-1;
|
||||||
|
31
src/resources/css/components/forms.css
Normal file
31
src/resources/css/components/forms.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.form-block {
|
||||||
|
@apply grid auto-rows-max gap-y-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
@apply uppercase font-semibold text-xs text-zinc-600 dark:text-zinc-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.form-input[type="date"],
|
||||||
|
input.form-input[type="datetime-local"],
|
||||||
|
input.form-input[type="email"],
|
||||||
|
input.form-input[type="month"],
|
||||||
|
input.form-input[type="number"],
|
||||||
|
input.form-input[type="password"],
|
||||||
|
input.form-input[type="search"],
|
||||||
|
input.form-input[type="tel"],
|
||||||
|
input.form-input[type="text"],
|
||||||
|
input.form-input[type="textarea"],
|
||||||
|
input.form-input[type="url"],
|
||||||
|
input.form-input[type="week"],
|
||||||
|
textarea.form-input {
|
||||||
|
@apply relative px-3 py-1 border rounded focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 bg-white dark:bg-indigo-900 text-zinc-900 dark:text-zinc-100 border-stone-300 dark:border-stone-600 shadow dark:shadow-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-checkbox-label {
|
||||||
|
@apply grid grid-flow-col auto-cols-max gap-x-2 items-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-checkbox-label .form-checkbox {
|
||||||
|
/*rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50*/
|
||||||
|
}
|
@ -1,7 +1,26 @@
|
|||||||
.table thead tr {
|
.table {
|
||||||
@apply border-b-2 border-gray-700 uppercase text-left;
|
@apply bg-white dark:bg-neutral-800 rounded w-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table thead th,
|
||||||
|
.table tbody td {
|
||||||
|
@apply px-4 py-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead tr {
|
||||||
|
@apply border-b border-neutral-900 dark:border-neutral-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
@apply text-zinc-700 dark:text-zinc-200 text-sm text-left font-semibold uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:not(:first-child) {
|
||||||
|
@apply border-t border-neutral-700 dark:border-neutral-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody td {}
|
||||||
|
|
||||||
.table-fuzzy-hover tbody:hover td {
|
.table-fuzzy-hover tbody:hover td {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
text-shadow: 0 0 2px #999;
|
text-shadow: 0 0 2px #999;
|
||||||
|
241
src/resources/css/text-shadows.css
Normal file
241
src/resources/css/text-shadows.css
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
.text-shadow-slate-50 { text-shadow: 1px 1px 2px #f8fafc; }
|
||||||
|
.text-shadow-slate-100 { text-shadow: 1px 1px 2px #f1f5f9; }
|
||||||
|
.text-shadow-slate-200 { text-shadow: 1px 1px 2px #e2e8f0; }
|
||||||
|
.text-shadow-slate-300 { text-shadow: 1px 1px 2px #cbd5e1; }
|
||||||
|
.text-shadow-slate-400 { text-shadow: 1px 1px 2px #94a3b8; }
|
||||||
|
.text-shadow-slate-500 { text-shadow: 1px 1px 2px #64748b; }
|
||||||
|
.text-shadow-slate-600 { text-shadow: 1px 1px 2px #475569; }
|
||||||
|
.text-shadow-slate-700 { text-shadow: 1px 1px 2px #334155; }
|
||||||
|
.text-shadow-slate-800 { text-shadow: 1px 1px 2px #1e293b; }
|
||||||
|
.text-shadow-slate-900 { text-shadow: 1px 1px 2px #0f172a; }
|
||||||
|
|
||||||
|
.text-shadow-gray-50 { text-shadow: 1px 1px 2px #f9fafb; }
|
||||||
|
.text-shadow-gray-100 { text-shadow: 1px 1px 2px #f3f4f6; }
|
||||||
|
.text-shadow-gray-200 { text-shadow: 1px 1px 2px #e5e7eb; }
|
||||||
|
.text-shadow-gray-300 { text-shadow: 1px 1px 2px #d1d5db; }
|
||||||
|
.text-shadow-gray-400 { text-shadow: 1px 1px 2px #9ca3af; }
|
||||||
|
.text-shadow-gray-500 { text-shadow: 1px 1px 2px #6b7280; }
|
||||||
|
.text-shadow-gray-600 { text-shadow: 1px 1px 2px #4b5563; }
|
||||||
|
.text-shadow-gray-700 { text-shadow: 1px 1px 2px #374151; }
|
||||||
|
.text-shadow-gray-800 { text-shadow: 1px 1px 2px #1f2937; }
|
||||||
|
.text-shadow-gray-900 { text-shadow: 1px 1px 2px #111827; }
|
||||||
|
|
||||||
|
.text-shadow-zinc-50 { text-shadow: 1px 1px 2px #fafafa; }
|
||||||
|
.text-shadow-zinc-100 { text-shadow: 1px 1px 2px #f4f4f5; }
|
||||||
|
.text-shadow-zinc-200 { text-shadow: 1px 1px 2px #e4e4e7; }
|
||||||
|
.text-shadow-zinc-300 { text-shadow: 1px 1px 2px #d4d4d8; }
|
||||||
|
.text-shadow-zinc-400 { text-shadow: 1px 1px 2px #a1a1aa; }
|
||||||
|
.text-shadow-zinc-500 { text-shadow: 1px 1px 2px #71717a; }
|
||||||
|
.text-shadow-zinc-600 { text-shadow: 1px 1px 2px #52525b; }
|
||||||
|
.text-shadow-zinc-700 { text-shadow: 1px 1px 2px #3f3f46; }
|
||||||
|
.text-shadow-zinc-800 { text-shadow: 1px 1px 2px #27272a; }
|
||||||
|
.text-shadow-zinc-900 { text-shadow: 1px 1px 2px #18181b; }
|
||||||
|
|
||||||
|
.text-shadow-neutral-50 { text-shadow: 1px 1px 2px #fafafa; }
|
||||||
|
.text-shadow-neutral-100 { text-shadow: 1px 1px 2px #f5f5f5; }
|
||||||
|
.text-shadow-neutral-200 { text-shadow: 1px 1px 2px #e5e5e5; }
|
||||||
|
.text-shadow-neutral-300 { text-shadow: 1px 1px 2px #d4d4d4; }
|
||||||
|
.text-shadow-neutral-400 { text-shadow: 1px 1px 2px #a3a3a3; }
|
||||||
|
.text-shadow-neutral-500 { text-shadow: 1px 1px 2px #737373; }
|
||||||
|
.text-shadow-neutral-600 { text-shadow: 1px 1px 2px #525252; }
|
||||||
|
.text-shadow-neutral-700 { text-shadow: 1px 1px 2px #404040; }
|
||||||
|
.text-shadow-neutral-800 { text-shadow: 1px 1px 2px #262626; }
|
||||||
|
.text-shadow-neutral-900 { text-shadow: 1px 1px 2px #171717; }
|
||||||
|
|
||||||
|
.text-shadow-stone-50 { text-shadow: 1px 1px 2px #fafaf9; }
|
||||||
|
.text-shadow-stone-100 { text-shadow: 1px 1px 2px #f5f5f4; }
|
||||||
|
.text-shadow-stone-200 { text-shadow: 1px 1px 2px #e7e5e4; }
|
||||||
|
.text-shadow-stone-300 { text-shadow: 1px 1px 2px #d6d3d1; }
|
||||||
|
.text-shadow-stone-400 { text-shadow: 1px 1px 2px #a8a29e; }
|
||||||
|
.text-shadow-stone-500 { text-shadow: 1px 1px 2px #78716c; }
|
||||||
|
.text-shadow-stone-600 { text-shadow: 1px 1px 2px #57534e; }
|
||||||
|
.text-shadow-stone-700 { text-shadow: 1px 1px 2px #44403c; }
|
||||||
|
.text-shadow-stone-800 { text-shadow: 1px 1px 2px #292524; }
|
||||||
|
.text-shadow-stone-900 { text-shadow: 1px 1px 2px #1c1917; }
|
||||||
|
|
||||||
|
.text-shadow-red-50 { text-shadow: 1px 1px 2px #fef2f2; }
|
||||||
|
.text-shadow-red-100 { text-shadow: 1px 1px 2px #fee2e2; }
|
||||||
|
.text-shadow-red-200 { text-shadow: 1px 1px 2px #fecaca; }
|
||||||
|
.text-shadow-red-300 { text-shadow: 1px 1px 2px #fca5a5; }
|
||||||
|
.text-shadow-red-400 { text-shadow: 1px 1px 2px #f87171; }
|
||||||
|
.text-shadow-red-500 { text-shadow: 1px 1px 2px #ef4444; }
|
||||||
|
.text-shadow-red-600 { text-shadow: 1px 1px 2px #dc2626; }
|
||||||
|
.text-shadow-red-700 { text-shadow: 1px 1px 2px #b91c1c; }
|
||||||
|
.text-shadow-red-800 { text-shadow: 1px 1px 2px #991b1b; }
|
||||||
|
.text-shadow-red-900 { text-shadow: 1px 1px 2px #7f1d1d; }
|
||||||
|
|
||||||
|
.text-shadow-orange-50 { text-shadow: 1px 1px 2px #fff7ed; }
|
||||||
|
.text-shadow-orange-100 { text-shadow: 1px 1px 2px #ffedd5; }
|
||||||
|
.text-shadow-orange-200 { text-shadow: 1px 1px 2px #fed7aa; }
|
||||||
|
.text-shadow-orange-300 { text-shadow: 1px 1px 2px #fdba74; }
|
||||||
|
.text-shadow-orange-400 { text-shadow: 1px 1px 2px #fb923c; }
|
||||||
|
.text-shadow-orange-500 { text-shadow: 1px 1px 2px #f97316; }
|
||||||
|
.text-shadow-orange-600 { text-shadow: 1px 1px 2px #ea580c; }
|
||||||
|
.text-shadow-orange-700 { text-shadow: 1px 1px 2px #c2410c; }
|
||||||
|
.text-shadow-orange-800 { text-shadow: 1px 1px 2px #9a3412; }
|
||||||
|
.text-shadow-orange-900 { text-shadow: 1px 1px 2px #7c2d12; }
|
||||||
|
|
||||||
|
.text-shadow-amber-50 { text-shadow: 1px 1px 2px #fffbeb; }
|
||||||
|
.text-shadow-amber-100 { text-shadow: 1px 1px 2px #fef3c7; }
|
||||||
|
.text-shadow-amber-200 { text-shadow: 1px 1px 2px #fde68a; }
|
||||||
|
.text-shadow-amber-300 { text-shadow: 1px 1px 2px #fcd34d; }
|
||||||
|
.text-shadow-amber-400 { text-shadow: 1px 1px 2px #fbbf24; }
|
||||||
|
.text-shadow-amber-500 { text-shadow: 1px 1px 2px #f59e0b; }
|
||||||
|
.text-shadow-amber-600 { text-shadow: 1px 1px 2px #d97706; }
|
||||||
|
.text-shadow-amber-700 { text-shadow: 1px 1px 2px #b45309; }
|
||||||
|
.text-shadow-amber-800 { text-shadow: 1px 1px 2px #92400e; }
|
||||||
|
.text-shadow-amber-900 { text-shadow: 1px 1px 2px #78350f; }
|
||||||
|
|
||||||
|
.text-shadow-yellow-50 { text-shadow: 1px 1px 2px #fefce8; }
|
||||||
|
.text-shadow-yellow-100 { text-shadow: 1px 1px 2px #fef9c3; }
|
||||||
|
.text-shadow-yellow-200 { text-shadow: 1px 1px 2px #fef08a; }
|
||||||
|
.text-shadow-yellow-300 { text-shadow: 1px 1px 2px #fde047; }
|
||||||
|
.text-shadow-yellow-400 { text-shadow: 1px 1px 2px #facc15; }
|
||||||
|
.text-shadow-yellow-500 { text-shadow: 1px 1px 2px #eab308; }
|
||||||
|
.text-shadow-yellow-600 { text-shadow: 1px 1px 2px #ca8a04; }
|
||||||
|
.text-shadow-yellow-700 { text-shadow: 1px 1px 2px #a16207; }
|
||||||
|
.text-shadow-yellow-800 { text-shadow: 1px 1px 2px #854d0e; }
|
||||||
|
.text-shadow-yellow-900 { text-shadow: 1px 1px 2px #713f12; }
|
||||||
|
|
||||||
|
.text-shadow-lime-50 { text-shadow: 1px 1px 2px #f7fee7; }
|
||||||
|
.text-shadow-lime-100 { text-shadow: 1px 1px 2px #ecfccb; }
|
||||||
|
.text-shadow-lime-200 { text-shadow: 1px 1px 2px #d9f99d; }
|
||||||
|
.text-shadow-lime-300 { text-shadow: 1px 1px 2px #bef264; }
|
||||||
|
.text-shadow-lime-400 { text-shadow: 1px 1px 2px #a3e635; }
|
||||||
|
.text-shadow-lime-500 { text-shadow: 1px 1px 2px #84cc16; }
|
||||||
|
.text-shadow-lime-600 { text-shadow: 1px 1px 2px #65a30d; }
|
||||||
|
.text-shadow-lime-700 { text-shadow: 1px 1px 2px #4d7c0f; }
|
||||||
|
.text-shadow-lime-800 { text-shadow: 1px 1px 2px #3f6212; }
|
||||||
|
.text-shadow-lime-900 { text-shadow: 1px 1px 2px #365314; }
|
||||||
|
|
||||||
|
.text-shadow-green-50 { text-shadow: 1px 1px 2px #f0fdf4; }
|
||||||
|
.text-shadow-green-100 { text-shadow: 1px 1px 2px #dcfce7; }
|
||||||
|
.text-shadow-green-200 { text-shadow: 1px 1px 2px #bbf7d0; }
|
||||||
|
.text-shadow-green-300 { text-shadow: 1px 1px 2px #86efac; }
|
||||||
|
.text-shadow-green-400 { text-shadow: 1px 1px 2px #4ade80; }
|
||||||
|
.text-shadow-green-500 { text-shadow: 1px 1px 2px #22c55e; }
|
||||||
|
.text-shadow-green-600 { text-shadow: 1px 1px 2px #16a34a; }
|
||||||
|
.text-shadow-green-700 { text-shadow: 1px 1px 2px #15803d; }
|
||||||
|
.text-shadow-green-800 { text-shadow: 1px 1px 2px #166534; }
|
||||||
|
.text-shadow-green-900 { text-shadow: 1px 1px 2px #14532d; }
|
||||||
|
|
||||||
|
.text-shadow-emerald-50 { text-shadow: 1px 1px 2px #ecfdf5; }
|
||||||
|
.text-shadow-emerald-100 { text-shadow: 1px 1px 2px #d1fae5; }
|
||||||
|
.text-shadow-emerald-200 { text-shadow: 1px 1px 2px #a7f3d0; }
|
||||||
|
.text-shadow-emerald-300 { text-shadow: 1px 1px 2px #6ee7b7; }
|
||||||
|
.text-shadow-emerald-400 { text-shadow: 1px 1px 2px #34d399; }
|
||||||
|
.text-shadow-emerald-500 { text-shadow: 1px 1px 2px #10b981; }
|
||||||
|
.text-shadow-emerald-600 { text-shadow: 1px 1px 2px #059669; }
|
||||||
|
.text-shadow-emerald-700 { text-shadow: 1px 1px 2px #047857; }
|
||||||
|
.text-shadow-emerald-800 { text-shadow: 1px 1px 2px #065f46; }
|
||||||
|
.text-shadow-emerald-900 { text-shadow: 1px 1px 2px #064e3b; }
|
||||||
|
|
||||||
|
.text-shadow-teal-50 { text-shadow: 1px 1px 2px #f0fdfa; }
|
||||||
|
.text-shadow-teal-100 { text-shadow: 1px 1px 2px #ccfbf1; }
|
||||||
|
.text-shadow-teal-200 { text-shadow: 1px 1px 2px #99f6e4; }
|
||||||
|
.text-shadow-teal-300 { text-shadow: 1px 1px 2px #5eead4; }
|
||||||
|
.text-shadow-teal-400 { text-shadow: 1px 1px 2px #2dd4bf; }
|
||||||
|
.text-shadow-teal-500 { text-shadow: 1px 1px 2px #14b8a6; }
|
||||||
|
.text-shadow-teal-600 { text-shadow: 1px 1px 2px #0d9488; }
|
||||||
|
.text-shadow-teal-700 { text-shadow: 1px 1px 2px #0f766e; }
|
||||||
|
.text-shadow-teal-800 { text-shadow: 1px 1px 2px #115e59; }
|
||||||
|
.text-shadow-teal-900 { text-shadow: 1px 1px 2px #134e4a; }
|
||||||
|
|
||||||
|
.text-shadow-cyan-50 { text-shadow: 1px 1px 2px #ecfeff; }
|
||||||
|
.text-shadow-cyan-100 { text-shadow: 1px 1px 2px #cffafe; }
|
||||||
|
.text-shadow-cyan-200 { text-shadow: 1px 1px 2px #a5f3fc; }
|
||||||
|
.text-shadow-cyan-300 { text-shadow: 1px 1px 2px #67e8f9; }
|
||||||
|
.text-shadow-cyan-400 { text-shadow: 1px 1px 2px #22d3ee; }
|
||||||
|
.text-shadow-cyan-500 { text-shadow: 1px 1px 2px #06b6d4; }
|
||||||
|
.text-shadow-cyan-600 { text-shadow: 1px 1px 2px #0891b2; }
|
||||||
|
.text-shadow-cyan-700 { text-shadow: 1px 1px 2px #0e7490; }
|
||||||
|
.text-shadow-cyan-800 { text-shadow: 1px 1px 2px #155e75; }
|
||||||
|
.text-shadow-cyan-900 { text-shadow: 1px 1px 2px #164e63; }
|
||||||
|
|
||||||
|
.text-shadow-sky-50 { text-shadow: 1px 1px 2px #f0f9ff; }
|
||||||
|
.text-shadow-sky-100 { text-shadow: 1px 1px 2px #e0f2fe; }
|
||||||
|
.text-shadow-sky-200 { text-shadow: 1px 1px 2px #bae6fd; }
|
||||||
|
.text-shadow-sky-300 { text-shadow: 1px 1px 2px #7dd3fc; }
|
||||||
|
.text-shadow-sky-400 { text-shadow: 1px 1px 2px #38bdf8; }
|
||||||
|
.text-shadow-sky-500 { text-shadow: 1px 1px 2px #0ea5e9; }
|
||||||
|
.text-shadow-sky-600 { text-shadow: 1px 1px 2px #0284c7; }
|
||||||
|
.text-shadow-sky-700 { text-shadow: 1px 1px 2px #0369a1; }
|
||||||
|
.text-shadow-sky-800 { text-shadow: 1px 1px 2px #075985; }
|
||||||
|
.text-shadow-sky-900 { text-shadow: 1px 1px 2px #0c4a6e; }
|
||||||
|
|
||||||
|
.text-shadow-blue-50 { text-shadow: 1px 1px 2px #eff6ff; }
|
||||||
|
.text-shadow-blue-100 { text-shadow: 1px 1px 2px #dbeafe; }
|
||||||
|
.text-shadow-blue-200 { text-shadow: 1px 1px 2px #bfdbfe; }
|
||||||
|
.text-shadow-blue-300 { text-shadow: 1px 1px 2px #93c5fd; }
|
||||||
|
.text-shadow-blue-400 { text-shadow: 1px 1px 2px #60a5fa; }
|
||||||
|
.text-shadow-blue-500 { text-shadow: 1px 1px 2px #3b82f6; }
|
||||||
|
.text-shadow-blue-600 { text-shadow: 1px 1px 2px #2563eb; }
|
||||||
|
.text-shadow-blue-700 { text-shadow: 1px 1px 2px #1d4ed8; }
|
||||||
|
.text-shadow-blue-800 { text-shadow: 1px 1px 2px #1e40af; }
|
||||||
|
.text-shadow-blue-900 { text-shadow: 1px 1px 2px #1e3a8a; }
|
||||||
|
|
||||||
|
.text-shadow-indigo-50 { text-shadow: 1px 1px 2px #eef2ff; }
|
||||||
|
.text-shadow-indigo-100 { text-shadow: 1px 1px 2px #e0e7ff; }
|
||||||
|
.text-shadow-indigo-200 { text-shadow: 1px 1px 2px #c7d2fe; }
|
||||||
|
.text-shadow-indigo-300 { text-shadow: 1px 1px 2px #a5b4fc; }
|
||||||
|
.text-shadow-indigo-400 { text-shadow: 1px 1px 2px #818cf8; }
|
||||||
|
.text-shadow-indigo-500 { text-shadow: 1px 1px 2px #6366f1; }
|
||||||
|
.text-shadow-indigo-600 { text-shadow: 1px 1px 2px #4f46e5; }
|
||||||
|
.text-shadow-indigo-700 { text-shadow: 1px 1px 2px #4338ca; }
|
||||||
|
.text-shadow-indigo-800 { text-shadow: 1px 1px 2px #3730a3; }
|
||||||
|
.text-shadow-indigo-900 { text-shadow: 1px 1px 2px #312e81; }
|
||||||
|
|
||||||
|
.text-shadow-violet-50 { text-shadow: 1px 1px 2px #f5f3ff; }
|
||||||
|
.text-shadow-violet-100 { text-shadow: 1px 1px 2px #ede9fe; }
|
||||||
|
.text-shadow-violet-200 { text-shadow: 1px 1px 2px #ddd6fe; }
|
||||||
|
.text-shadow-violet-300 { text-shadow: 1px 1px 2px #c4b5fd; }
|
||||||
|
.text-shadow-violet-400 { text-shadow: 1px 1px 2px #a78bfa; }
|
||||||
|
.text-shadow-violet-500 { text-shadow: 1px 1px 2px #8b5cf6; }
|
||||||
|
.text-shadow-violet-600 { text-shadow: 1px 1px 2px #7c3aed; }
|
||||||
|
.text-shadow-violet-700 { text-shadow: 1px 1px 2px #6d28d9; }
|
||||||
|
.text-shadow-violet-800 { text-shadow: 1px 1px 2px #5b21b6; }
|
||||||
|
.text-shadow-violet-900 { text-shadow: 1px 1px 2px #4c1d95; }
|
||||||
|
|
||||||
|
.text-shadow-purple-50 { text-shadow: 1px 1px 2px #faf5ff; }
|
||||||
|
.text-shadow-purple-100 { text-shadow: 1px 1px 2px #f3e8ff; }
|
||||||
|
.text-shadow-purple-200 { text-shadow: 1px 1px 2px #e9d5ff; }
|
||||||
|
.text-shadow-purple-300 { text-shadow: 1px 1px 2px #d8b4fe; }
|
||||||
|
.text-shadow-purple-400 { text-shadow: 1px 1px 2px #c084fc; }
|
||||||
|
.text-shadow-purple-500 { text-shadow: 1px 1px 2px #a855f7; }
|
||||||
|
.text-shadow-purple-600 { text-shadow: 1px 1px 2px #9333ea; }
|
||||||
|
.text-shadow-purple-700 { text-shadow: 1px 1px 2px #7e22ce; }
|
||||||
|
.text-shadow-purple-800 { text-shadow: 1px 1px 2px #6b21a8; }
|
||||||
|
.text-shadow-purple-900 { text-shadow: 1px 1px 2px #581c87; }
|
||||||
|
|
||||||
|
.text-shadow-fuchsia-50 { text-shadow: 1px 1px 2px #fdf4ff; }
|
||||||
|
.text-shadow-fuchsia-100 { text-shadow: 1px 1px 2px #fae8ff; }
|
||||||
|
.text-shadow-fuchsia-200 { text-shadow: 1px 1px 2px #f5d0fe; }
|
||||||
|
.text-shadow-fuchsia-300 { text-shadow: 1px 1px 2px #f0abfc; }
|
||||||
|
.text-shadow-fuchsia-400 { text-shadow: 1px 1px 2px #e879f9; }
|
||||||
|
.text-shadow-fuchsia-500 { text-shadow: 1px 1px 2px #d946ef; }
|
||||||
|
.text-shadow-fuchsia-600 { text-shadow: 1px 1px 2px #c026d3; }
|
||||||
|
.text-shadow-fuchsia-700 { text-shadow: 1px 1px 2px #a21caf; }
|
||||||
|
.text-shadow-fuchsia-800 { text-shadow: 1px 1px 2px #86198f; }
|
||||||
|
.text-shadow-fuchsia-900 { text-shadow: 1px 1px 2px #701a75; }
|
||||||
|
|
||||||
|
.text-shadow-pink-50 { text-shadow: 1px 1px 2px #fdf2f8; }
|
||||||
|
.text-shadow-pink-100 { text-shadow: 1px 1px 2px #fce7f3; }
|
||||||
|
.text-shadow-pink-200 { text-shadow: 1px 1px 2px #fbcfe8; }
|
||||||
|
.text-shadow-pink-300 { text-shadow: 1px 1px 2px #f9a8d4; }
|
||||||
|
.text-shadow-pink-400 { text-shadow: 1px 1px 2px #f472b6; }
|
||||||
|
.text-shadow-pink-500 { text-shadow: 1px 1px 2px #ec4899; }
|
||||||
|
.text-shadow-pink-600 { text-shadow: 1px 1px 2px #db2777; }
|
||||||
|
.text-shadow-pink-700 { text-shadow: 1px 1px 2px #be185d; }
|
||||||
|
.text-shadow-pink-800 { text-shadow: 1px 1px 2px #9d174d; }
|
||||||
|
.text-shadow-pink-900 { text-shadow: 1px 1px 2px #831843; }
|
||||||
|
|
||||||
|
.text-shadow-rose-50 { text-shadow: 1px 1px 2px #fff1f2; }
|
||||||
|
.text-shadow-rose-100 { text-shadow: 1px 1px 2px #ffe4e6; }
|
||||||
|
.text-shadow-rose-200 { text-shadow: 1px 1px 2px #fecdd3; }
|
||||||
|
.text-shadow-rose-300 { text-shadow: 1px 1px 2px #fda4af; }
|
||||||
|
.text-shadow-rose-400 { text-shadow: 1px 1px 2px #fb7185; }
|
||||||
|
.text-shadow-rose-500 { text-shadow: 1px 1px 2px #f43f5e; }
|
||||||
|
.text-shadow-rose-600 { text-shadow: 1px 1px 2px #e11d48; }
|
||||||
|
.text-shadow-rose-700 { text-shadow: 1px 1px 2px #be123c; }
|
||||||
|
.text-shadow-rose-800 { text-shadow: 1px 1px 2px #9f1239; }
|
||||||
|
.text-shadow-rose-900 { text-shadow: 1px 1px 2px #881337; }
|
107
src/resources/js/Pages/Profile-old/Partials/DeleteUserForm.vue
Normal file
107
src/resources/js/Pages/Profile-old/Partials/DeleteUserForm.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<jet-action-section>
|
||||||
|
<template #title>
|
||||||
|
Delete Account
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #description>
|
||||||
|
Permanently delete your account.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<div class="max-w-xl text-sm text-gray-600">
|
||||||
|
Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<jet-danger-button @click="confirmUserDeletion">
|
||||||
|
Delete Account
|
||||||
|
</jet-danger-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Account Confirmation Modal -->
|
||||||
|
<jet-dialog-modal :show="confirmingUserDeletion" @close="closeModal">
|
||||||
|
<template #title>
|
||||||
|
Delete Account
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
Are you sure you want to delete your account? Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<jet-input type="password" class="mt-1 block w-3/4" placeholder="Password"
|
||||||
|
ref="password"
|
||||||
|
v-model="form.password"
|
||||||
|
@keyup.enter="deleteUser" />
|
||||||
|
|
||||||
|
<jet-input-error :message="form.errors.password" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<jet-secondary-button @click="closeModal">
|
||||||
|
Cancel
|
||||||
|
</jet-secondary-button>
|
||||||
|
|
||||||
|
<jet-danger-button class="ml-2" @click="deleteUser" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Delete Account
|
||||||
|
</jet-danger-button>
|
||||||
|
</template>
|
||||||
|
</jet-dialog-modal>
|
||||||
|
</template>
|
||||||
|
</jet-action-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
||||||
|
import JetDialogModal from '@/Jetstream/DialogModal.vue'
|
||||||
|
import JetDangerButton from '@/Jetstream/DangerButton.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
JetActionSection,
|
||||||
|
JetDangerButton,
|
||||||
|
JetDialogModal,
|
||||||
|
JetInput,
|
||||||
|
JetInputError,
|
||||||
|
JetSecondaryButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
confirmingUserDeletion: false,
|
||||||
|
|
||||||
|
form: this.$inertia.form({
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
confirmUserDeletion() {
|
||||||
|
this.confirmingUserDeletion = true;
|
||||||
|
|
||||||
|
setTimeout(() => this.$refs.password.focus(), 250)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteUser() {
|
||||||
|
this.form.delete(route('current-user.destroy'), {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => this.closeModal(),
|
||||||
|
onError: () => this.$refs.password.focus(),
|
||||||
|
onFinish: () => this.form.reset(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
closeModal() {
|
||||||
|
this.confirmingUserDeletion = false
|
||||||
|
|
||||||
|
this.form.reset()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<jet-action-section>
|
||||||
|
<template #title>
|
||||||
|
Browser Sessions
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #description>
|
||||||
|
Manage and log out your active sessions on other browsers and devices.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<div class="max-w-xl text-sm text-gray-600">
|
||||||
|
If necessary, you may log out of all of your other browser sessions across all of your devices. Some of your recent sessions are listed below; however, this list may not be exhaustive. If you feel your account has been compromised, you should also update your password.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Other Browser Sessions -->
|
||||||
|
<div class="mt-5 space-y-6" v-if="sessions.length > 0">
|
||||||
|
<div class="flex items-center" v-for="(session, i) in sessions" :key="i">
|
||||||
|
<div>
|
||||||
|
<svg fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" class="w-8 h-8 text-gray-500" v-if="session.agent.is_desktop">
|
||||||
|
<path d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" class="w-8 h-8 text-gray-500" v-else>
|
||||||
|
<path d="M0 0h24v24H0z" stroke="none"></path><rect x="7" y="4" width="10" height="16" rx="1"></rect><path d="M11 5h2M12 17v.01"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-3">
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
{{ session.agent.platform }} - {{ session.agent.browser }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-gray-500">
|
||||||
|
{{ session.ip_address }},
|
||||||
|
|
||||||
|
<span class="text-green-500 font-semibold" v-if="session.is_current_device">This device</span>
|
||||||
|
<span v-else>Last active {{ session.last_active }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center mt-5">
|
||||||
|
<jet-button @click="confirmLogout">
|
||||||
|
Log Out Other Browser Sessions
|
||||||
|
</jet-button>
|
||||||
|
|
||||||
|
<jet-action-message :on="form.recentlySuccessful" class="ml-3">
|
||||||
|
Done.
|
||||||
|
</jet-action-message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Log Out Other Devices Confirmation Modal -->
|
||||||
|
<jet-dialog-modal :show="confirmingLogout" @close="closeModal">
|
||||||
|
<template #title>
|
||||||
|
Log Out Other Browser Sessions
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
Please enter your password to confirm you would like to log out of your other browser sessions across all of your devices.
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<jet-input type="password" class="mt-1 block w-3/4" placeholder="Password"
|
||||||
|
ref="password"
|
||||||
|
v-model="form.password"
|
||||||
|
@keyup.enter="logoutOtherBrowserSessions" />
|
||||||
|
|
||||||
|
<jet-input-error :message="form.errors.password" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<jet-secondary-button @click="closeModal">
|
||||||
|
Cancel
|
||||||
|
</jet-secondary-button>
|
||||||
|
|
||||||
|
<jet-button class="ml-2" @click="logoutOtherBrowserSessions" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Log Out Other Browser Sessions
|
||||||
|
</jet-button>
|
||||||
|
</template>
|
||||||
|
</jet-dialog-modal>
|
||||||
|
</template>
|
||||||
|
</jet-action-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
||||||
|
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
||||||
|
import JetButton from '@/Jetstream/Button.vue'
|
||||||
|
import JetDialogModal from '@/Jetstream/DialogModal.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: ['sessions'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
JetActionMessage,
|
||||||
|
JetActionSection,
|
||||||
|
JetButton,
|
||||||
|
JetDialogModal,
|
||||||
|
JetInput,
|
||||||
|
JetInputError,
|
||||||
|
JetSecondaryButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
confirmingLogout: false,
|
||||||
|
|
||||||
|
form: this.$inertia.form({
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
confirmLogout() {
|
||||||
|
this.confirmingLogout = true
|
||||||
|
|
||||||
|
setTimeout(() => this.$refs.password.focus(), 250)
|
||||||
|
},
|
||||||
|
|
||||||
|
logoutOtherBrowserSessions() {
|
||||||
|
this.form.delete(route('other-browser-sessions.destroy'), {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => this.closeModal(),
|
||||||
|
onError: () => this.$refs.password.focus(),
|
||||||
|
onFinish: () => this.form.reset(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
closeModal() {
|
||||||
|
this.confirmingLogout = false
|
||||||
|
|
||||||
|
this.form.reset()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,167 @@
|
|||||||
|
<template>
|
||||||
|
<jet-action-section>
|
||||||
|
<template #title>
|
||||||
|
Two Factor Authentication
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #description>
|
||||||
|
Add additional security to your account using two factor authentication.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<h3 class="text-lg font-medium text-gray-900" v-if="twoFactorEnabled">
|
||||||
|
You have enabled two factor authentication.
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<h3 class="text-lg font-medium text-gray-900" v-else>
|
||||||
|
You have not enabled two factor authentication.
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="mt-3 max-w-xl text-sm text-gray-600">
|
||||||
|
<p>
|
||||||
|
When two factor authentication is enabled, you will be prompted for a secure, random token during authentication. You may retrieve this token from your phone's Google Authenticator application.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="twoFactorEnabled">
|
||||||
|
<div v-if="qrCode">
|
||||||
|
<div class="mt-4 max-w-xl text-sm text-gray-600">
|
||||||
|
<p class="font-semibold">
|
||||||
|
Two factor authentication is now enabled. Scan the following QR code using your phone's authenticator application.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4" v-html="qrCode">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="recoveryCodes.length > 0">
|
||||||
|
<div class="mt-4 max-w-xl text-sm text-gray-600">
|
||||||
|
<p class="font-semibold">
|
||||||
|
Store these recovery codes in a secure password manager. They can be used to recover access to your account if your two factor authentication device is lost.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-1 max-w-xl mt-4 px-4 py-4 font-mono text-sm bg-gray-100 rounded-lg">
|
||||||
|
<div v-for="code in recoveryCodes" :key="code">
|
||||||
|
{{ code }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<div v-if="! twoFactorEnabled">
|
||||||
|
<jet-confirms-password @confirmed="enableTwoFactorAuthentication">
|
||||||
|
<jet-button type="button" :class="{ 'opacity-25': enabling }" :disabled="enabling">
|
||||||
|
Enable
|
||||||
|
</jet-button>
|
||||||
|
</jet-confirms-password>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<jet-confirms-password @confirmed="regenerateRecoveryCodes">
|
||||||
|
<jet-secondary-button class="mr-3"
|
||||||
|
v-if="recoveryCodes.length > 0">
|
||||||
|
Regenerate Recovery Codes
|
||||||
|
</jet-secondary-button>
|
||||||
|
</jet-confirms-password>
|
||||||
|
|
||||||
|
<jet-confirms-password @confirmed="showRecoveryCodes">
|
||||||
|
<jet-secondary-button class="mr-3" v-if="recoveryCodes.length === 0">
|
||||||
|
Show Recovery Codes
|
||||||
|
</jet-secondary-button>
|
||||||
|
</jet-confirms-password>
|
||||||
|
|
||||||
|
<jet-confirms-password @confirmed="disableTwoFactorAuthentication">
|
||||||
|
<jet-danger-button
|
||||||
|
:class="{ 'opacity-25': disabling }"
|
||||||
|
:disabled="disabling">
|
||||||
|
Disable
|
||||||
|
</jet-danger-button>
|
||||||
|
</jet-confirms-password>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</jet-action-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
||||||
|
import JetButton from '@/Jetstream/Button.vue'
|
||||||
|
import JetConfirmsPassword from '@/Jetstream/ConfirmsPassword.vue'
|
||||||
|
import JetDangerButton from '@/Jetstream/DangerButton.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
JetActionSection,
|
||||||
|
JetButton,
|
||||||
|
JetConfirmsPassword,
|
||||||
|
JetDangerButton,
|
||||||
|
JetSecondaryButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
enabling: false,
|
||||||
|
disabling: false,
|
||||||
|
|
||||||
|
qrCode: null,
|
||||||
|
recoveryCodes: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
enableTwoFactorAuthentication() {
|
||||||
|
this.enabling = true
|
||||||
|
|
||||||
|
this.$inertia.post('/user/two-factor-authentication', {}, {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => Promise.all([
|
||||||
|
this.showQrCode(),
|
||||||
|
this.showRecoveryCodes(),
|
||||||
|
]),
|
||||||
|
onFinish: () => (this.enabling = false),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
showQrCode() {
|
||||||
|
return axios.get('/user/two-factor-qr-code')
|
||||||
|
.then(response => {
|
||||||
|
this.qrCode = response.data.svg
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
showRecoveryCodes() {
|
||||||
|
return axios.get('/user/two-factor-recovery-codes')
|
||||||
|
.then(response => {
|
||||||
|
this.recoveryCodes = response.data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
regenerateRecoveryCodes() {
|
||||||
|
axios.post('/user/two-factor-recovery-codes')
|
||||||
|
.then(response => {
|
||||||
|
this.showRecoveryCodes()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
disableTwoFactorAuthentication() {
|
||||||
|
this.disabling = true
|
||||||
|
|
||||||
|
this.$inertia.delete('/user/two-factor-authentication', {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => (this.disabling = false),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
twoFactorEnabled() {
|
||||||
|
return ! this.enabling && this.$page.props.user.two_factor_enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<jet-form-section @submitted="updatePassword">
|
||||||
|
<template #title>
|
||||||
|
Update Password
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #description>
|
||||||
|
Ensure your account is using a long, random password to stay secure.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #form>
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="current_password" value="Current Password" />
|
||||||
|
<jet-input id="current_password" type="password" class="mt-1 block w-full" v-model="form.current_password" ref="current_password" autocomplete="current-password" />
|
||||||
|
<jet-input-error :message="form.errors.current_password" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="password" value="New Password" />
|
||||||
|
<jet-input id="password" type="password" class="mt-1 block w-full" v-model="form.password" ref="password" autocomplete="new-password" />
|
||||||
|
<jet-input-error :message="form.errors.password" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="password_confirmation" value="Confirm Password" />
|
||||||
|
<jet-input id="password_confirmation" type="password" class="mt-1 block w-full" v-model="form.password_confirmation" autocomplete="new-password" />
|
||||||
|
<jet-input-error :message="form.errors.password_confirmation" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<jet-action-message :on="form.recentlySuccessful" class="mr-3">
|
||||||
|
Saved.
|
||||||
|
</jet-action-message>
|
||||||
|
|
||||||
|
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Save
|
||||||
|
</jet-button>
|
||||||
|
</template>
|
||||||
|
</jet-form-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
||||||
|
import JetButton from '@/Jetstream/Button.vue'
|
||||||
|
import JetFormSection from '@/Jetstream/FormSection.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetLabel from '@/Jetstream/Label.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
JetActionMessage,
|
||||||
|
JetButton,
|
||||||
|
JetFormSection,
|
||||||
|
JetInput,
|
||||||
|
JetInputError,
|
||||||
|
JetLabel,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: this.$inertia.form({
|
||||||
|
current_password: '',
|
||||||
|
password: '',
|
||||||
|
password_confirmation: '',
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updatePassword() {
|
||||||
|
this.form.put(route('user-password.update'), {
|
||||||
|
errorBag: 'updatePassword',
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => this.form.reset(),
|
||||||
|
onError: () => {
|
||||||
|
if (this.form.errors.password) {
|
||||||
|
this.form.reset('password', 'password_confirmation')
|
||||||
|
this.$refs.password.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.form.errors.current_password) {
|
||||||
|
this.form.reset('current_password')
|
||||||
|
this.$refs.current_password.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
@ -0,0 +1,174 @@
|
|||||||
|
<template>
|
||||||
|
<jet-form-section @submitted="updateProfileInformation">
|
||||||
|
<template #title>
|
||||||
|
Profile Information
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #description>
|
||||||
|
Update your account's profile information and email address.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #form>
|
||||||
|
<!-- Profile Photo -->
|
||||||
|
<div class="col-span-6 sm:col-span-4" v-if="$page.props.jetstream.managesProfilePhotos">
|
||||||
|
<!-- Profile Photo File Input -->
|
||||||
|
<input type="file" class="hidden"
|
||||||
|
ref="photo"
|
||||||
|
@change="updatePhotoPreview">
|
||||||
|
|
||||||
|
<jet-label for="photo" value="Photo" />
|
||||||
|
|
||||||
|
<!-- Current Profile Photo -->
|
||||||
|
<div class="mt-2" v-show="! photoPreview">
|
||||||
|
<img :src="user.profile_photo_url" :alt="user.name" class="rounded-full h-20 w-20 object-cover">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- New Profile Photo Preview -->
|
||||||
|
<div class="mt-2" v-show="photoPreview">
|
||||||
|
<span class="block rounded-full w-20 h-20 bg-cover bg-no-repeat bg-center"
|
||||||
|
:style="'background-image: url(\'' + photoPreview + '\');'">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<jet-secondary-button class="mt-2 mr-2" type="button" @click.prevent="selectNewPhoto">
|
||||||
|
Select A New Photo
|
||||||
|
</jet-secondary-button>
|
||||||
|
|
||||||
|
<jet-secondary-button type="button" class="mt-2" @click.prevent="deletePhoto" v-if="user.profile_photo_path">
|
||||||
|
Remove Photo
|
||||||
|
</jet-secondary-button>
|
||||||
|
|
||||||
|
<jet-input-error :message="form.errors.photo" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Name -->
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="name" value="Name" />
|
||||||
|
<jet-input id="name" type="text" class="mt-1 block w-full" v-model="form.name" autocomplete="given-name" />
|
||||||
|
<jet-input-error :message="form.errors.name" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Surname -->
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="surname" value="Surname" />
|
||||||
|
<jet-input id="surname" type="text" class="mt-1 block w-full" v-model="form.surname" autocomplete="family-name" />
|
||||||
|
<jet-input-error :message="form.errors.surname" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="email" value="Email" />
|
||||||
|
<jet-input id="email" type="email" class="mt-1 block w-full" v-model="form.email" />
|
||||||
|
<jet-input-error :message="form.errors.email" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Timezone -->
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<jet-label for="timezone_name" value="Timezone" />
|
||||||
|
<jet-input id="timezone_name" type="text" list="" class="mt-1 block w-full" v-model="form.timezone_name" />
|
||||||
|
<jet-input-error :message="form.errors.timezone_name" class="mt-2" />
|
||||||
|
<datalist id="timezones">
|
||||||
|
<option v-for="timezone in timezones" :key="timezone" :value="timezone"></option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<jet-action-message :on="form.recentlySuccessful" class="mr-3">
|
||||||
|
Saved.
|
||||||
|
</jet-action-message>
|
||||||
|
|
||||||
|
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Save
|
||||||
|
</jet-button>
|
||||||
|
</template>
|
||||||
|
</jet-form-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import JetButton from '@/Jetstream/Button.vue'
|
||||||
|
import JetFormSection from '@/Jetstream/FormSection.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetLabel from '@/Jetstream/Label.vue'
|
||||||
|
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
JetActionMessage,
|
||||||
|
JetButton,
|
||||||
|
JetFormSection,
|
||||||
|
JetInput,
|
||||||
|
JetInputError,
|
||||||
|
JetLabel,
|
||||||
|
JetSecondaryButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['user', 'timezones'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: this.$inertia.form({
|
||||||
|
_method: 'PUT',
|
||||||
|
name: this.user.name,
|
||||||
|
surname: this.user.surname,
|
||||||
|
email: this.user.email,
|
||||||
|
timezone_name: this.user.timezone_name,
|
||||||
|
photo: null,
|
||||||
|
}),
|
||||||
|
|
||||||
|
photoPreview: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateProfileInformation() {
|
||||||
|
if (this.$refs.photo) {
|
||||||
|
this.form.photo = this.$refs.photo.files[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.form.post(route('user-profile-information.update'), {
|
||||||
|
errorBag: 'updateProfileInformation',
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => (this.clearPhotoFileInput()),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
selectNewPhoto() {
|
||||||
|
this.$refs.photo.click();
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePhotoPreview() {
|
||||||
|
const photo = this.$refs.photo.files[0];
|
||||||
|
|
||||||
|
if (! photo) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = (e) => {
|
||||||
|
this.photoPreview = e.target.result;
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(photo);
|
||||||
|
},
|
||||||
|
|
||||||
|
deletePhoto() {
|
||||||
|
this.$inertia.delete(route('current-user-photo.destroy'), {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => {
|
||||||
|
this.photoPreview = null;
|
||||||
|
this.clearPhotoFileInput();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearPhotoFileInput() {
|
||||||
|
if (this.$refs.photo?.value) {
|
||||||
|
this.$refs.photo.value = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
68
src/resources/js/Pages/Profile-old/Show.vue
Normal file
68
src/resources/js/Pages/Profile-old/Show.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<app-layout title="Profile">
|
||||||
|
<template #header>
|
||||||
|
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||||
|
Profile
|
||||||
|
</h2>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
|
||||||
|
<div v-if="$page.props.jetstream.canUpdateProfileInformation">
|
||||||
|
<update-profile-information-form :user="$page.props.user" />
|
||||||
|
|
||||||
|
<jet-section-border />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="$page.props.jetstream.canUpdatePassword">
|
||||||
|
<update-password-form class="mt-10 sm:mt-0" />
|
||||||
|
|
||||||
|
<jet-section-border />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="$page.props.jetstream.canManageTwoFactorAuthentication">
|
||||||
|
<two-factor-authentication-form class="mt-10 sm:mt-0" />
|
||||||
|
|
||||||
|
<jet-section-border />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<logout-other-browser-sessions-form :sessions="sessions" class="mt-10 sm:mt-0" />
|
||||||
|
|
||||||
|
<template v-if="$page.props.jetstream.hasAccountDeletionFeatures">
|
||||||
|
<jet-section-border />
|
||||||
|
|
||||||
|
<delete-user-form class="mt-10 sm:mt-0" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</app-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import AppLayout from '@/Layouts/AppLayout.vue'
|
||||||
|
import DeleteUserForm from '@/Pages/Profile/Partials/DeleteUserForm.vue'
|
||||||
|
import JetSectionBorder from '@/Jetstream/SectionBorder.vue'
|
||||||
|
import LogoutOtherBrowserSessionsForm from '@/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue'
|
||||||
|
import TwoFactorAuthenticationForm from '@/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue'
|
||||||
|
import UpdatePasswordForm from '@/Pages/Profile/Partials/UpdatePasswordForm.vue'
|
||||||
|
import UpdateProfileInformationForm from '@/Pages/Profile/Partials/UpdateProfileInformationForm.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
sessions: Array,
|
||||||
|
timezones: Array,
|
||||||
|
allTimes: Array,
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
AppLayout,
|
||||||
|
DeleteUserForm,
|
||||||
|
JetSectionBorder,
|
||||||
|
LogoutOtherBrowserSessionsForm,
|
||||||
|
TwoFactorAuthenticationForm,
|
||||||
|
UpdatePasswordForm,
|
||||||
|
UpdateProfileInformationForm,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
@ -1,107 +1,82 @@
|
|||||||
<template>
|
<script setup>
|
||||||
<jet-action-section>
|
import { ref } from 'vue'
|
||||||
<template #title>
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
Delete Account
|
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
||||||
</template>
|
import JetDialogModal from '@/Jetstream/DialogModal.vue'
|
||||||
|
import JetDangerButton from '@/Jetstream/DangerButton.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
<template #description>
|
const confirmingUserDeletion = ref(false)
|
||||||
Permanently delete your account.
|
const passwordInput = ref(null)
|
||||||
</template>
|
|
||||||
|
const form = useForm({
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed properties
|
||||||
|
|
||||||
|
// watchers
|
||||||
|
|
||||||
|
// lifecycle hooks
|
||||||
|
|
||||||
|
// methods
|
||||||
|
const confirmUserDeletion = () => {
|
||||||
|
confirmingUserDeletion.value = true
|
||||||
|
setTimeout(() => passwordInput.value.focus(), 250)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
confirmingUserDeletion.value = false
|
||||||
|
form.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteUser = () => {
|
||||||
|
form.delete(route('current-user.destroy'), {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => closeModal(),
|
||||||
|
onError: () => passwordInput.value.focus(),
|
||||||
|
onFinish: () => form.reset(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<JetActionSection>
|
||||||
|
<template #title>Delete Account</template>
|
||||||
|
|
||||||
|
<template #description>Permanently delete your account.</template>
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="max-w-xl text-sm text-gray-600">
|
<div class="max-w-xl">
|
||||||
Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.
|
<p class="text-sm text-gray-600">Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<jet-danger-button @click="confirmUserDeletion">
|
<JetDangerButton @click="confirmUserDeletion">Delete Account</JetDangerButton>
|
||||||
Delete Account
|
|
||||||
</jet-danger-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Delete Account Confirmation Modal -->
|
<!-- Delete Account Confirmation Modal -->
|
||||||
<jet-dialog-modal :show="confirmingUserDeletion" @close="closeModal">
|
<JetDialogModal :show="confirmingUserDeletion" @close="closeModal">
|
||||||
<template #title>
|
<template #title>Delete Account</template>
|
||||||
Delete Account
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
Are you sure you want to delete your account? Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.
|
<p>Are you sure you want to delete your account? Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.</p>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<jet-input type="password" class="mt-1 block w-3/4" placeholder="Password"
|
<JetInput type="password" ref="passwordInput" placeholder="Password" v-model="form.password" class="mt-1 block w-3/4" @keyup.enter="deleteUser" />
|
||||||
ref="password"
|
<JetInputError :message="form.errors.password" class="mt-2" />
|
||||||
v-model="form.password"
|
|
||||||
@keyup.enter="deleteUser" />
|
|
||||||
|
|
||||||
<jet-input-error :message="form.errors.password" class="mt-2" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<jet-secondary-button @click="closeModal">
|
<JetSecondaryButton @click="closeModal">Cancel</JetSecondaryButton>
|
||||||
Cancel
|
|
||||||
</jet-secondary-button>
|
|
||||||
|
|
||||||
<jet-danger-button class="ml-2" @click="deleteUser" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
<JetDangerButton class="ml-3" :class="{ 'opacity-25': form.processing }" :disabled="form.processing" @click="deleteUser"
|
||||||
Delete Account
|
>Delete Account</JetDangerButton>
|
||||||
</jet-danger-button>
|
|
||||||
</template>
|
</template>
|
||||||
</jet-dialog-modal>
|
</JetDialogModal>
|
||||||
</template>
|
</template>
|
||||||
</jet-action-section>
|
</JetActionSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
|
||||||
import JetDialogModal from '@/Jetstream/DialogModal.vue'
|
|
||||||
import JetDangerButton from '@/Jetstream/DangerButton.vue'
|
|
||||||
import JetInput from '@/Jetstream/Input.vue'
|
|
||||||
import JetInputError from '@/Jetstream/InputError.vue'
|
|
||||||
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
JetActionSection,
|
|
||||||
JetDangerButton,
|
|
||||||
JetDialogModal,
|
|
||||||
JetInput,
|
|
||||||
JetInputError,
|
|
||||||
JetSecondaryButton,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
confirmingUserDeletion: false,
|
|
||||||
|
|
||||||
form: this.$inertia.form({
|
|
||||||
password: '',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
confirmUserDeletion() {
|
|
||||||
this.confirmingUserDeletion = true;
|
|
||||||
|
|
||||||
setTimeout(() => this.$refs.password.focus(), 250)
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteUser() {
|
|
||||||
this.form.delete(route('current-user.destroy'), {
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => this.closeModal(),
|
|
||||||
onError: () => this.$refs.password.focus(),
|
|
||||||
onFinish: () => this.form.reset(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
closeModal() {
|
|
||||||
this.confirmingUserDeletion = false
|
|
||||||
|
|
||||||
this.form.reset()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
@ -1,5 +1,54 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
|
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
||||||
|
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
||||||
|
import JetButton from '@/Jetstream/Button.vue'
|
||||||
|
import JetDialogModal from '@/Jetstream/DialogModal.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
sessions: Array,
|
||||||
|
})
|
||||||
|
|
||||||
|
const confirmingLogout = ref(false)
|
||||||
|
const passwordInput = ref(null)
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed properties
|
||||||
|
|
||||||
|
// watchers
|
||||||
|
|
||||||
|
// lifecycle hooks
|
||||||
|
|
||||||
|
// methods
|
||||||
|
const confirmLogout = () => {
|
||||||
|
confirmingLogout.value = true
|
||||||
|
setTimeout(() => passwordInput.value.focus(), 250)
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoutOtherBrowserSessions = () => {
|
||||||
|
form.delete(route('other-browser-sessions.destroy'), {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => closeModal(),
|
||||||
|
onError: () => passwordInput.value.focus(),
|
||||||
|
onFinish: () => form.reset(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
confirmingLogout.value = false
|
||||||
|
form.reset()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<jet-action-section>
|
<JetActionSection>
|
||||||
<template #title>
|
<template #title>
|
||||||
Browser Sessions
|
Browser Sessions
|
||||||
</template>
|
</template>
|
||||||
@ -14,28 +63,53 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Other Browser Sessions -->
|
<!-- Other Browser Sessions -->
|
||||||
<div class="mt-5 space-y-6" v-if="sessions.length > 0">
|
<div v-if="sessions.length > 0" class="mt-5 space-y-6">
|
||||||
<div class="flex items-center" v-for="(session, i) in sessions" :key="i">
|
<div v-for="(session, i) in sessions" :key="i" class="flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<svg fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" class="w-8 h-8 text-gray-500" v-if="session.agent.is_desktop">
|
<svg
|
||||||
<path d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
v-if="session.agent.is_desktop"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="w-8 h-8 text-gray-500"
|
||||||
|
>
|
||||||
|
<path d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" class="w-8 h-8 text-gray-500" v-else>
|
<svg
|
||||||
<path d="M0 0h24v24H0z" stroke="none"></path><rect x="7" y="4" width="10" height="16" rx="1"></rect><path d="M11 5h2M12 17v.01"></path>
|
v-else
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="w-8 h-8 text-gray-500"
|
||||||
|
>
|
||||||
|
<path d="M0 0h24v24H0z" stroke="none" /><rect
|
||||||
|
x="7"
|
||||||
|
y="4"
|
||||||
|
width="10"
|
||||||
|
height="16"
|
||||||
|
rx="1"
|
||||||
|
/><path d="M11 5h2M12 17v.01" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<div class="text-sm text-gray-600">
|
<div class="text-sm text-gray-600">
|
||||||
{{ session.agent.platform }} - {{ session.agent.browser }}
|
{{ session.agent.platform ? session.agent.platform : 'Unknown' }} - {{ session.agent.browser ? session.agent.browser : 'Unknown' }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs text-gray-500">
|
<div class="text-xs text-gray-500">
|
||||||
{{ session.ip_address }},
|
{{ session.ip_address }},
|
||||||
|
|
||||||
<span class="text-green-500 font-semibold" v-if="session.is_current_device">This device</span>
|
<span v-if="session.is_current_device" class="text-green-500 font-semibold">This device</span>
|
||||||
<span v-else>Last active {{ session.last_active }}</span>
|
<span v-else>Last active {{ session.last_active }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -44,17 +118,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center mt-5">
|
<div class="flex items-center mt-5">
|
||||||
<jet-button @click="confirmLogout">
|
<JetButton @click="confirmLogout">
|
||||||
Log Out Other Browser Sessions
|
Log Out Other Browser Sessions
|
||||||
</jet-button>
|
</JetButton>
|
||||||
|
|
||||||
<jet-action-message :on="form.recentlySuccessful" class="ml-3">
|
<JetActionMessage :on="form.recentlySuccessful" class="ml-3">
|
||||||
Done.
|
Done.
|
||||||
</jet-action-message>
|
</JetActionMessage>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Log Out Other Devices Confirmation Modal -->
|
<!-- Log Out Other Devices Confirmation Modal -->
|
||||||
<jet-dialog-modal :show="confirmingLogout" @close="closeModal">
|
<JetDialogModal :show="confirmingLogout" @close="closeModal">
|
||||||
<template #title>
|
<template #title>
|
||||||
Log Out Other Browser Sessions
|
Log Out Other Browser Sessions
|
||||||
</template>
|
</template>
|
||||||
@ -63,83 +137,34 @@
|
|||||||
Please enter your password to confirm you would like to log out of your other browser sessions across all of your devices.
|
Please enter your password to confirm you would like to log out of your other browser sessions across all of your devices.
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<jet-input type="password" class="mt-1 block w-3/4" placeholder="Password"
|
<JetInput
|
||||||
ref="password"
|
ref="passwordInput"
|
||||||
v-model="form.password"
|
v-model="form.password"
|
||||||
@keyup.enter="logoutOtherBrowserSessions" />
|
type="password"
|
||||||
|
class="mt-1 block w-3/4"
|
||||||
|
placeholder="Password"
|
||||||
|
@keyup.enter="logoutOtherBrowserSessions"
|
||||||
|
/>
|
||||||
|
|
||||||
<jet-input-error :message="form.errors.password" class="mt-2" />
|
<JetInputError :message="form.errors.password" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<jet-secondary-button @click="closeModal">
|
<JetSecondaryButton @click="closeModal">
|
||||||
Cancel
|
Cancel
|
||||||
</jet-secondary-button>
|
</JetSecondaryButton>
|
||||||
|
|
||||||
<jet-button class="ml-2" @click="logoutOtherBrowserSessions" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
<JetButton
|
||||||
|
class="ml-3"
|
||||||
|
:class="{ 'opacity-25': form.processing }"
|
||||||
|
:disabled="form.processing"
|
||||||
|
@click="logoutOtherBrowserSessions"
|
||||||
|
>
|
||||||
Log Out Other Browser Sessions
|
Log Out Other Browser Sessions
|
||||||
</jet-button>
|
</JetButton>
|
||||||
</template>
|
</template>
|
||||||
</jet-dialog-modal>
|
</JetDialogModal>
|
||||||
</template>
|
</template>
|
||||||
</jet-action-section>
|
</JetActionSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
|
||||||
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
|
||||||
import JetButton from '@/Jetstream/Button.vue'
|
|
||||||
import JetDialogModal from '@/Jetstream/DialogModal.vue'
|
|
||||||
import JetInput from '@/Jetstream/Input.vue'
|
|
||||||
import JetInputError from '@/Jetstream/InputError.vue'
|
|
||||||
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: ['sessions'],
|
|
||||||
|
|
||||||
components: {
|
|
||||||
JetActionMessage,
|
|
||||||
JetActionSection,
|
|
||||||
JetButton,
|
|
||||||
JetDialogModal,
|
|
||||||
JetInput,
|
|
||||||
JetInputError,
|
|
||||||
JetSecondaryButton,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
confirmingLogout: false,
|
|
||||||
|
|
||||||
form: this.$inertia.form({
|
|
||||||
password: '',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
confirmLogout() {
|
|
||||||
this.confirmingLogout = true
|
|
||||||
|
|
||||||
setTimeout(() => this.$refs.password.focus(), 250)
|
|
||||||
},
|
|
||||||
|
|
||||||
logoutOtherBrowserSessions() {
|
|
||||||
this.form.delete(route('other-browser-sessions.destroy'), {
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => this.closeModal(),
|
|
||||||
onError: () => this.$refs.password.focus(),
|
|
||||||
onFinish: () => this.form.reset(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
closeModal() {
|
|
||||||
this.confirmingLogout = false
|
|
||||||
|
|
||||||
this.form.reset()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
@ -1,167 +1,117 @@
|
|||||||
<template>
|
<script setup>
|
||||||
<jet-action-section>
|
import { ref, computed, watch } from 'vue'
|
||||||
<template #title>
|
import { Inertia } from '@inertiajs/inertia'
|
||||||
Two Factor Authentication
|
import { useForm, usePage } from '@inertiajs/inertia-vue3'
|
||||||
</template>
|
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
||||||
|
import JetButton from '@/Jetstream/Button.vue'
|
||||||
|
import JetConfirmsPassword from '@/Jetstream/ConfirmsPassword.vue'
|
||||||
|
import JetDangerButton from '@/Jetstream/DangerButton.vue'
|
||||||
|
import JetInput from '@/Jetstream/Input.vue'
|
||||||
|
import JetInputError from '@/Jetstream/InputError.vue'
|
||||||
|
import JetLabel from '@/Jetstream/Label.vue'
|
||||||
|
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
||||||
|
|
||||||
<template #description>
|
const props = defineProps({
|
||||||
Add additional security to your account using two factor authentication.
|
requiresConfirmation: Boolean,
|
||||||
</template>
|
})
|
||||||
|
|
||||||
<template #content>
|
const enabling = ref(false)
|
||||||
<h3 class="text-lg font-medium text-gray-900" v-if="twoFactorEnabled">
|
const confirming = ref(false)
|
||||||
You have enabled two factor authentication.
|
const disabling = ref(false)
|
||||||
</h3>
|
const qrCode = ref(null)
|
||||||
|
const setupKey = ref(null)
|
||||||
|
const recoveryCodes = ref([])
|
||||||
|
|
||||||
<h3 class="text-lg font-medium text-gray-900" v-else>
|
const confirmationForm = useForm({
|
||||||
You have not enabled two factor authentication.
|
code: '',
|
||||||
</h3>
|
})
|
||||||
|
|
||||||
<div class="mt-3 max-w-xl text-sm text-gray-600">
|
// computed properties
|
||||||
<p>
|
const twoFactorEnabled = computed(
|
||||||
When two factor authentication is enabled, you will be prompted for a secure, random token during authentication. You may retrieve this token from your phone's Google Authenticator application.
|
() => ! enabling.value && usePage().props.value.user?.two_factor_enabled,
|
||||||
</p>
|
)
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="twoFactorEnabled">
|
// watchers
|
||||||
<div v-if="qrCode">
|
watch(twoFactorEnabled, () => {
|
||||||
<div class="mt-4 max-w-xl text-sm text-gray-600">
|
if (! twoFactorEnabled.value) {
|
||||||
<p class="font-semibold">
|
confirmationForm.reset()
|
||||||
Two factor authentication is now enabled. Scan the following QR code using your phone's authenticator application.
|
confirmationForm.clearErrors()
|
||||||
</p>
|
}
|
||||||
</div>
|
})
|
||||||
|
|
||||||
<div class="mt-4" v-html="qrCode">
|
// lifecycle hooks
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="recoveryCodes.length > 0">
|
// methods
|
||||||
<div class="mt-4 max-w-xl text-sm text-gray-600">
|
const enableTwoFactorAuthentication = () => {
|
||||||
<p class="font-semibold">
|
enabling.value = true
|
||||||
Store these recovery codes in a secure password manager. They can be used to recover access to your account if your two factor authentication device is lost.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid gap-1 max-w-xl mt-4 px-4 py-4 font-mono text-sm bg-gray-100 rounded-lg">
|
Inertia.post('/user/two-factor-authentication', {}, {
|
||||||
<div v-for="code in recoveryCodes" :key="code">
|
preserveScroll: true,
|
||||||
{{ code }}
|
onSuccess: () => Promise.all([
|
||||||
</div>
|
showQrCode(),
|
||||||
</div>
|
showSetupKey(),
|
||||||
</div>
|
showRecoveryCodes(),
|
||||||
</div>
|
]),
|
||||||
|
onFinish: () => {
|
||||||
|
enabling.value = false;
|
||||||
|
confirming.value = props.requiresConfirmation
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
<div class="mt-5">
|
const showQrCode = () => {
|
||||||
<div v-if="! twoFactorEnabled">
|
return axios.get('/user/two-factor-qr-code')
|
||||||
<jet-confirms-password @confirmed="enableTwoFactorAuthentication">
|
.then(response => {
|
||||||
<jet-button type="button" :class="{ 'opacity-25': enabling }" :disabled="enabling">
|
qrCode.value = response.data.svg
|
||||||
Enable
|
|
||||||
</jet-button>
|
|
||||||
</jet-confirms-password>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else>
|
|
||||||
<jet-confirms-password @confirmed="regenerateRecoveryCodes">
|
|
||||||
<jet-secondary-button class="mr-3"
|
|
||||||
v-if="recoveryCodes.length > 0">
|
|
||||||
Regenerate Recovery Codes
|
|
||||||
</jet-secondary-button>
|
|
||||||
</jet-confirms-password>
|
|
||||||
|
|
||||||
<jet-confirms-password @confirmed="showRecoveryCodes">
|
|
||||||
<jet-secondary-button class="mr-3" v-if="recoveryCodes.length === 0">
|
|
||||||
Show Recovery Codes
|
|
||||||
</jet-secondary-button>
|
|
||||||
</jet-confirms-password>
|
|
||||||
|
|
||||||
<jet-confirms-password @confirmed="disableTwoFactorAuthentication">
|
|
||||||
<jet-danger-button
|
|
||||||
:class="{ 'opacity-25': disabling }"
|
|
||||||
:disabled="disabling">
|
|
||||||
Disable
|
|
||||||
</jet-danger-button>
|
|
||||||
</jet-confirms-password>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</jet-action-section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import JetActionSection from '@/Jetstream/ActionSection.vue'
|
|
||||||
import JetButton from '@/Jetstream/Button.vue'
|
|
||||||
import JetConfirmsPassword from '@/Jetstream/ConfirmsPassword.vue'
|
|
||||||
import JetDangerButton from '@/Jetstream/DangerButton.vue'
|
|
||||||
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
JetActionSection,
|
|
||||||
JetButton,
|
|
||||||
JetConfirmsPassword,
|
|
||||||
JetDangerButton,
|
|
||||||
JetSecondaryButton,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
enabling: false,
|
|
||||||
disabling: false,
|
|
||||||
|
|
||||||
qrCode: null,
|
|
||||||
recoveryCodes: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
enableTwoFactorAuthentication() {
|
|
||||||
this.enabling = true
|
|
||||||
|
|
||||||
this.$inertia.post('/user/two-factor-authentication', {}, {
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => Promise.all([
|
|
||||||
this.showQrCode(),
|
|
||||||
this.showRecoveryCodes(),
|
|
||||||
]),
|
|
||||||
onFinish: () => (this.enabling = false),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
showQrCode() {
|
|
||||||
return axios.get('/user/two-factor-qr-code')
|
|
||||||
.then(response => {
|
|
||||||
this.qrCode = response.data.svg
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
showRecoveryCodes() {
|
|
||||||
return axios.get('/user/two-factor-recovery-codes')
|
|
||||||
.then(response => {
|
|
||||||
this.recoveryCodes = response.data
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
regenerateRecoveryCodes() {
|
|
||||||
axios.post('/user/two-factor-recovery-codes')
|
|
||||||
.then(response => {
|
|
||||||
this.showRecoveryCodes()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
disableTwoFactorAuthentication() {
|
|
||||||
this.disabling = true
|
|
||||||
|
|
||||||
this.$inertia.delete('/user/two-factor-authentication', {
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => (this.disabling = false),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
twoFactorEnabled() {
|
|
||||||
return ! this.enabling && this.$page.props.user.two_factor_enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showSetupKey = () => {
|
||||||
|
return axios.get('/user/two-factor-secret-key')
|
||||||
|
.then(response => {
|
||||||
|
setupKey.value = response.data.secretKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showRecoveryCodes = () => {
|
||||||
|
return axios.get('/user/two-factor-recovery-codes')
|
||||||
|
.then(response => {
|
||||||
|
recoveryCodes.value = response.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmTwoFactorAuthentication = () => {
|
||||||
|
confirmationForm.post('/user/confirmed-two-factor-authentication', {
|
||||||
|
errorBag: "confirmTwoFactorAuthentication",
|
||||||
|
preserveScroll: true,
|
||||||
|
preserveState: true,
|
||||||
|
onSuccess: () => {
|
||||||
|
confirming.value = false
|
||||||
|
qrCode.value = null
|
||||||
|
setupKey.value = null
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const regenerateRecoveryCodes = () => {
|
||||||
|
axios
|
||||||
|
.post('/user/two-factor-recovery-codes')
|
||||||
|
.then(() => showRecoveryCodes())
|
||||||
|
}
|
||||||
|
|
||||||
|
const disableTwoFactorAuthentication = () => {
|
||||||
|
disabling.value = true
|
||||||
|
|
||||||
|
Inertia.delete('/user/two-factor-authentication', {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => {
|
||||||
|
disabling.value = false
|
||||||
|
confirming.value = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
@ -1,93 +1,19 @@
|
|||||||
<template>
|
<script setup>
|
||||||
<jet-form-section @submitted="updatePassword">
|
import { reactive, ref, computed, watch, onBeforeMount, onMounted } from 'vue'
|
||||||
<template #title>
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
Update Password
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #description>
|
const props = defineProps({})
|
||||||
Ensure your account is using a long, random password to stay secure.
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #form>
|
// computed properties
|
||||||
<div class="col-span-6 sm:col-span-4">
|
|
||||||
<jet-label for="current_password" value="Current Password" />
|
|
||||||
<jet-input id="current_password" type="password" class="mt-1 block w-full" v-model="form.current_password" ref="current_password" autocomplete="current-password" />
|
|
||||||
<jet-input-error :message="form.errors.current_password" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
// watchers
|
||||||
<jet-label for="password" value="New Password" />
|
|
||||||
<jet-input id="password" type="password" class="mt-1 block w-full" v-model="form.password" ref="password" autocomplete="new-password" />
|
|
||||||
<jet-input-error :message="form.errors.password" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
// lifecycle hooks
|
||||||
<jet-label for="password_confirmation" value="Confirm Password" />
|
|
||||||
<jet-input id="password_confirmation" type="password" class="mt-1 block w-full" v-model="form.password_confirmation" autocomplete="new-password" />
|
|
||||||
<jet-input-error :message="form.errors.password_confirmation" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #actions>
|
// methods
|
||||||
<jet-action-message :on="form.recentlySuccessful" class="mr-3">
|
|
||||||
Saved.
|
|
||||||
</jet-action-message>
|
|
||||||
|
|
||||||
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
|
||||||
Save
|
|
||||||
</jet-button>
|
|
||||||
</template>
|
|
||||||
</jet-form-section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
|
||||||
import JetButton from '@/Jetstream/Button.vue'
|
|
||||||
import JetFormSection from '@/Jetstream/FormSection.vue'
|
|
||||||
import JetInput from '@/Jetstream/Input.vue'
|
|
||||||
import JetInputError from '@/Jetstream/InputError.vue'
|
|
||||||
import JetLabel from '@/Jetstream/Label.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
JetActionMessage,
|
|
||||||
JetButton,
|
|
||||||
JetFormSection,
|
|
||||||
JetInput,
|
|
||||||
JetInputError,
|
|
||||||
JetLabel,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
form: this.$inertia.form({
|
|
||||||
current_password: '',
|
|
||||||
password: '',
|
|
||||||
password_confirmation: '',
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updatePassword() {
|
|
||||||
this.form.put(route('user-password.update'), {
|
|
||||||
errorBag: 'updatePassword',
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => this.form.reset(),
|
|
||||||
onError: () => {
|
|
||||||
if (this.form.errors.password) {
|
|
||||||
this.form.reset('password', 'password_confirmation')
|
|
||||||
this.$refs.password.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.form.errors.current_password) {
|
|
||||||
this.form.reset('current_password')
|
|
||||||
this.$refs.current_password.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
@ -1,174 +1,19 @@
|
|||||||
<template>
|
<script setup>
|
||||||
<jet-form-section @submitted="updateProfileInformation">
|
import { reactive, ref, computed, watch, onBeforeMount, onMounted } from 'vue'
|
||||||
<template #title>
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
Profile Information
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #description>
|
const props = defineProps({})
|
||||||
Update your account's profile information and email address.
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #form>
|
// computed properties
|
||||||
<!-- Profile Photo -->
|
|
||||||
<div class="col-span-6 sm:col-span-4" v-if="$page.props.jetstream.managesProfilePhotos">
|
|
||||||
<!-- Profile Photo File Input -->
|
|
||||||
<input type="file" class="hidden"
|
|
||||||
ref="photo"
|
|
||||||
@change="updatePhotoPreview">
|
|
||||||
|
|
||||||
<jet-label for="photo" value="Photo" />
|
// watchers
|
||||||
|
|
||||||
<!-- Current Profile Photo -->
|
// lifecycle hooks
|
||||||
<div class="mt-2" v-show="! photoPreview">
|
|
||||||
<img :src="user.profile_photo_url" :alt="user.name" class="rounded-full h-20 w-20 object-cover">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- New Profile Photo Preview -->
|
// methods
|
||||||
<div class="mt-2" v-show="photoPreview">
|
|
||||||
<span class="block rounded-full w-20 h-20 bg-cover bg-no-repeat bg-center"
|
|
||||||
:style="'background-image: url(\'' + photoPreview + '\');'">
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<jet-secondary-button class="mt-2 mr-2" type="button" @click.prevent="selectNewPhoto">
|
|
||||||
Select A New Photo
|
|
||||||
</jet-secondary-button>
|
|
||||||
|
|
||||||
<jet-secondary-button type="button" class="mt-2" @click.prevent="deletePhoto" v-if="user.profile_photo_path">
|
|
||||||
Remove Photo
|
|
||||||
</jet-secondary-button>
|
|
||||||
|
|
||||||
<jet-input-error :message="form.errors.photo" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Name -->
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
|
||||||
<jet-label for="name" value="Name" />
|
|
||||||
<jet-input id="name" type="text" class="mt-1 block w-full" v-model="form.name" autocomplete="given-name" />
|
|
||||||
<jet-input-error :message="form.errors.name" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Surname -->
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
|
||||||
<jet-label for="surname" value="Surname" />
|
|
||||||
<jet-input id="surname" type="text" class="mt-1 block w-full" v-model="form.surname" autocomplete="family-name" />
|
|
||||||
<jet-input-error :message="form.errors.surname" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email -->
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
|
||||||
<jet-label for="email" value="Email" />
|
|
||||||
<jet-input id="email" type="email" class="mt-1 block w-full" v-model="form.email" />
|
|
||||||
<jet-input-error :message="form.errors.email" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Timezone -->
|
|
||||||
<div class="col-span-6 sm:col-span-4">
|
|
||||||
<jet-label for="timezone_name" value="Timezone" />
|
|
||||||
<jet-input id="timezone_name" type="text" list="" class="mt-1 block w-full" v-model="form.timezone_name" />
|
|
||||||
<jet-input-error :message="form.errors.timezone_name" class="mt-2" />
|
|
||||||
<datalist id="timezones">
|
|
||||||
<option v-for="timezone in timezones" :key="timezone" :value="timezone"></option>
|
|
||||||
</datalist>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #actions>
|
|
||||||
<jet-action-message :on="form.recentlySuccessful" class="mr-3">
|
|
||||||
Saved.
|
|
||||||
</jet-action-message>
|
|
||||||
|
|
||||||
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
|
||||||
Save
|
|
||||||
</jet-button>
|
|
||||||
</template>
|
|
||||||
</jet-form-section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import JetButton from '@/Jetstream/Button.vue'
|
|
||||||
import JetFormSection from '@/Jetstream/FormSection.vue'
|
|
||||||
import JetInput from '@/Jetstream/Input.vue'
|
|
||||||
import JetInputError from '@/Jetstream/InputError.vue'
|
|
||||||
import JetLabel from '@/Jetstream/Label.vue'
|
|
||||||
import JetActionMessage from '@/Jetstream/ActionMessage.vue'
|
|
||||||
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
JetActionMessage,
|
|
||||||
JetButton,
|
|
||||||
JetFormSection,
|
|
||||||
JetInput,
|
|
||||||
JetInputError,
|
|
||||||
JetLabel,
|
|
||||||
JetSecondaryButton,
|
|
||||||
},
|
|
||||||
|
|
||||||
props: ['user', 'timezones'],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
form: this.$inertia.form({
|
|
||||||
_method: 'PUT',
|
|
||||||
name: this.user.name,
|
|
||||||
surname: this.user.surname,
|
|
||||||
email: this.user.email,
|
|
||||||
timezone_name: this.user.timezone_name,
|
|
||||||
photo: null,
|
|
||||||
}),
|
|
||||||
|
|
||||||
photoPreview: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateProfileInformation() {
|
|
||||||
if (this.$refs.photo) {
|
|
||||||
this.form.photo = this.$refs.photo.files[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.form.post(route('user-profile-information.update'), {
|
|
||||||
errorBag: 'updateProfileInformation',
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => (this.clearPhotoFileInput()),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
selectNewPhoto() {
|
|
||||||
this.$refs.photo.click();
|
|
||||||
},
|
|
||||||
|
|
||||||
updatePhotoPreview() {
|
|
||||||
const photo = this.$refs.photo.files[0];
|
|
||||||
|
|
||||||
if (! photo) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
|
|
||||||
reader.onload = (e) => {
|
|
||||||
this.photoPreview = e.target.result;
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsDataURL(photo);
|
|
||||||
},
|
|
||||||
|
|
||||||
deletePhoto() {
|
|
||||||
this.$inertia.delete(route('current-user-photo.destroy'), {
|
|
||||||
preserveScroll: true,
|
|
||||||
onSuccess: () => {
|
|
||||||
this.photoPreview = null;
|
|
||||||
this.clearPhotoFileInput();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
clearPhotoFileInput() {
|
|
||||||
if (this.$refs.photo?.value) {
|
|
||||||
this.$refs.photo.value = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
@ -1,68 +1,53 @@
|
|||||||
<template>
|
<script setup>
|
||||||
<app-layout title="Profile">
|
import { reactive, ref, computed, watch, onBeforeMount, onMounted } from 'vue'
|
||||||
<template #header>
|
|
||||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
|
||||||
Profile
|
|
||||||
</h2>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
|
|
||||||
<div v-if="$page.props.jetstream.canUpdateProfileInformation">
|
|
||||||
<update-profile-information-form :user="$page.props.user" />
|
|
||||||
|
|
||||||
<jet-section-border />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="$page.props.jetstream.canUpdatePassword">
|
|
||||||
<update-password-form class="mt-10 sm:mt-0" />
|
|
||||||
|
|
||||||
<jet-section-border />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="$page.props.jetstream.canManageTwoFactorAuthentication">
|
|
||||||
<two-factor-authentication-form class="mt-10 sm:mt-0" />
|
|
||||||
|
|
||||||
<jet-section-border />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<logout-other-browser-sessions-form :sessions="sessions" class="mt-10 sm:mt-0" />
|
|
||||||
|
|
||||||
<template v-if="$page.props.jetstream.hasAccountDeletionFeatures">
|
|
||||||
<jet-section-border />
|
|
||||||
|
|
||||||
<delete-user-form class="mt-10 sm:mt-0" />
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</app-layout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import AppLayout from '@/Layouts/AppLayout.vue'
|
import AppLayout from '@/Layouts/AppLayout.vue'
|
||||||
import DeleteUserForm from '@/Pages/Profile/Partials/DeleteUserForm.vue'
|
import DeleteUserForm from '@/Pages/Profile/Partials/DeleteUserForm.vue'
|
||||||
import JetSectionBorder from '@/Jetstream/SectionBorder.vue'
|
|
||||||
import LogoutOtherBrowserSessionsForm from '@/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue'
|
import LogoutOtherBrowserSessionsForm from '@/Pages/Profile/Partials/LogoutOtherBrowserSessionsForm.vue'
|
||||||
import TwoFactorAuthenticationForm from '@/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue'
|
import TwoFactorAuthenticationForm from '@/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue'
|
||||||
import UpdatePasswordForm from '@/Pages/Profile/Partials/UpdatePasswordForm.vue'
|
import UpdatePasswordForm from '@/Pages/Profile/Partials/UpdatePasswordForm.vue'
|
||||||
import UpdateProfileInformationForm from '@/Pages/Profile/Partials/UpdateProfileInformationForm.vue'
|
import UpdateProfileInformationForm from '@/Pages/Profile/Partials/UpdateProfileInformationForm.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
const props = defineProps({
|
||||||
props: {
|
confirmsTwoFactorAuthentication: Boolean,
|
||||||
sessions: Array,
|
sessions: Array,
|
||||||
timezones: Array,
|
timezones: Array,
|
||||||
allTimes: Array,
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
AppLayout,
|
|
||||||
DeleteUserForm,
|
|
||||||
JetSectionBorder,
|
|
||||||
LogoutOtherBrowserSessionsForm,
|
|
||||||
TwoFactorAuthenticationForm,
|
|
||||||
UpdatePasswordForm,
|
|
||||||
UpdateProfileInformationForm,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// computed properties
|
||||||
|
|
||||||
|
// watchers
|
||||||
|
|
||||||
|
// lifecycle hooks
|
||||||
|
|
||||||
|
// methods
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<app-layout title="Profile">
|
||||||
|
<template #header>Profile</template>
|
||||||
|
|
||||||
|
<div class="col-span-6">
|
||||||
|
<div v-if="$page.props.jetstream.canUpdateProfileInformation">
|
||||||
|
<UpdateProfileInformationForm :user="$page.props.user" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="$page.props.jetstream.canUpdatePassword">
|
||||||
|
<UpdatePasswordForm class="mt-10 sm:mt-0" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="$page.props.jetstream.canManageTwoFactorAuthentication">
|
||||||
|
<TwoFactorAuthenticationForm
|
||||||
|
:requires-confirmation="confirmsTwoFactorAuthentication"
|
||||||
|
class="mt-10 sm:mt-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LogoutOtherBrowserSessionsForm :sessions="sessions" class="mt-10 sm:mt-0" />
|
||||||
|
|
||||||
|
<template v-if="$page.props.jetstream.hasAccountDeletionFeatures">
|
||||||
|
<DeleteUserForm class="mt-10 sm:mt-0" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</app-layout>
|
||||||
|
</template>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Set the base URL for all relative URLs within the document -->
|
<!-- Set the base URL for all relative URLs within the document -->
|
||||||
<base href="https://example.com/">
|
<base href="{{ url('/') }}">
|
||||||
|
|
||||||
<meta name="robots" content="index,follow">
|
<meta name="robots" content="index,follow">
|
||||||
<meta name="googlebot" content="index,follow">
|
<meta name="googlebot" content="index,follow">
|
||||||
@ -17,32 +17,32 @@
|
|||||||
|
|
||||||
<!-- Prefetching, preloading, prebrowsing -->
|
<!-- Prefetching, preloading, prebrowsing -->
|
||||||
<meta http-equiv="x-dns-prefetch-control" content="off">
|
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||||
<link rel="dns-prefetch" href="//example.com/">
|
<link rel="dns-prefetch" href="{{ config('app.domain', 'localhost') }}">
|
||||||
<link rel="preconnect" href="https://www.example.com/">
|
<link rel="preconnect" href="{{ url('/') }}">
|
||||||
<link rel="prefetch" href="https://www.example.com/">
|
<link rel="prefetch" href="{{ url('/') }}">
|
||||||
<link rel="prerender" href="https://example.com/">
|
<link rel="prerender" href="{{ url('/') }}">
|
||||||
<link rel="preload" href="image.png" as="image">
|
<link rel="preload" href="image.png" as="image">
|
||||||
|
|
||||||
<meta name="rating" content="General">
|
<meta name="rating" content="General">
|
||||||
|
|
||||||
<meta name="url" content="{{ url('/') }}">
|
<meta name="url" content="{{ url('/') }}">
|
||||||
<meta name="subject" content="your website's subject">
|
<meta name="subject" content="{{ $pageSubject ?? "your website's subject" }}">
|
||||||
<meta name="description" content="A description of the page">
|
<meta name="description" content="{{ $pageDescription ?? "A description of the page" }}">
|
||||||
|
|
||||||
<title inertia>{{ config('app.name', 'Laravel') }}</title>
|
<title inertia>{{ config('app.name', 'Laravel') }}</title>
|
||||||
|
|
||||||
<!-- Name of web application (only should be used if the website is used as an app) -->
|
<!-- Name of web application (only should be used if the website is used as an app) -->
|
||||||
<meta name="application-name" content="Application Name">
|
<!-- <meta name="application-name" content="Application Name"> -->
|
||||||
|
|
||||||
<!-- Privacy -->
|
<!-- Privacy -->
|
||||||
<meta name="twitter:dnt" content="on">
|
<meta name="twitter:dnt" content="on">
|
||||||
<meta name="pinterest" content="nopin" description="Sorry, you can't save from my website!">
|
<meta name="pinterest" content="nopin" description="No pinning allowed.">
|
||||||
|
|
||||||
<!-- analytics -->
|
<!-- analytics -->
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
|
||||||
<!-- Helps prevent duplicate content issues -->
|
<!-- Helps prevent duplicate content issues -->
|
||||||
<link href="{{ url()->full() }}" rel="canonical">
|
<link href="{{ url()->current() }}" rel="canonical">
|
||||||
|
|
||||||
<!-- Gives a reference to a location in your document that may be in another language -->
|
<!-- Gives a reference to a location in your document that may be in another language -->
|
||||||
{{-- <link href="https://de.example.com/2010/06/title-of-my-article" rel="alternate" hreflang="de"> --}}
|
{{-- <link href="https://de.example.com/2010/06/title-of-my-article" rel="alternate" hreflang="de"> --}}
|
||||||
@ -59,8 +59,8 @@
|
|||||||
{{-- <link href="{{ url('/feed.atom') }}" type="application/atom+xml" title="Atom 0.3" rel="alternate"> --}}
|
{{-- <link href="{{ url('/feed.atom') }}" type="application/atom+xml" title="Atom 0.3" rel="alternate"> --}}
|
||||||
|
|
||||||
<!-- oEmbed links -->
|
<!-- oEmbed links -->
|
||||||
<link rel="alternate" type="application/json+oembed" href="https://example.com/services/oembed?url=http%3A%2F%2Fexample.com%2Ffoo%2F&format=json" title="oEmbed Profile: JSON">
|
{{-- <link rel="alternate" type="application/json+oembed" href="https://example.com/services/oembed?url={{ urlencode(url()->current()) }}&format=json" title="oEmbed Profile: JSON"> --}}
|
||||||
<link rel="alternate" type="text/xml+oembed" href="https://example.com/services/oembed?url=http%3A%2F%2Fexample.com%2Ffoo%2F&format=xml" title="oEmbed Profile: XML">
|
{{-- <link rel="alternate" type="text/xml+oembed" href="https://example.com/services/oembed?url={{ urlencode(url()->current()) }}&format=xml" title="oEmbed Profile: XML"> --}}
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
{{-- <link href="{{ asset('/favicon.ico') }}" rel="icon" sizes="16x16" type="image/icon" integrity="{{ env('INTEGRITY_HASH_FAVICON_ICO') }}"> --}}
|
{{-- <link href="{{ asset('/favicon.ico') }}" rel="icon" sizes="16x16" type="image/icon" integrity="{{ env('INTEGRITY_HASH_FAVICON_ICO') }}"> --}}
|
||||||
@ -68,7 +68,12 @@
|
|||||||
{{-- <link href="{{ asset('/favicon.png') }}" rel="icon" sizes="192x192" integrity="{{ env('INTEGRITY_HASH_FAVICON_PNG') }}"> --}}
|
{{-- <link href="{{ asset('/favicon.png') }}" rel="icon" sizes="192x192" integrity="{{ env('INTEGRITY_HASH_FAVICON_PNG') }}"> --}}
|
||||||
|
|
||||||
<!-- Font preloads (should be done for each font file) -->
|
<!-- Font preloads (should be done for each font file) -->
|
||||||
<link href="{{ asset('/fonts/Nunito/Nunito-Regular.woff2') }}" rel="preload" as="font" type="font/woff2" integrity="{{ env('INTEGRITY_HASH_NUNITO_REGULAR_WOFF2_FONT') }}" crossorigin="anonymous">
|
<link href="{{ asset('/fonts/Nunito/Nunito-Bold.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link href="{{ asset('/fonts/Nunito/Nunito-SemiBold.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link href="{{ asset('/fonts/Nunito/Nunito-Regular.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link href="{{ asset('/fonts/OpenSans/OpenSans-Bold.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link href="{{ asset('/fonts/OpenSans/OpenSans-SemiBold.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link href="{{ asset('/fonts/OpenSans/OpenSans-Regular.woff2') }}" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link href="{{ mix('/css/app.css') }}" rel="stylesheet" integrity="{{ env('INTEGRITY_HASH_APP_CSS') }}" media="screen">
|
<link href="{{ mix('/css/app.css') }}" rel="stylesheet" integrity="{{ env('INTEGRITY_HASH_APP_CSS') }}" media="screen">
|
||||||
@ -86,7 +91,7 @@
|
|||||||
<script src="{{ mix('/js/app.js') }}" integrity="{{ env('INTEGRITY_HASH_APP_JS') }}" defer></script>
|
<script src="{{ mix('/js/app.js') }}" integrity="{{ env('INTEGRITY_HASH_APP_JS') }}" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-100 font-sans antialiased">
|
<body class="bg-gray-100 font-sans antialiased">
|
||||||
<x-country-flags></x-country-flags>
|
<x-flags></x-flags>
|
||||||
|
|
||||||
@inertia
|
@inertia
|
||||||
|
|
||||||
|
@ -1,10 +1 @@
|
|||||||
<meta property="fb:app_id" content="123456789">
|
<meta property="fb:app_id" content="123456789">
|
||||||
<meta property="og:url" content="https://example.com/page.html">
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta property="og:title" content="Content Title">
|
|
||||||
<meta property="og:image" content="https://example.com/image.jpg">
|
|
||||||
<meta property="og:image:alt" content="A description of what is in the image (not a caption)">
|
|
||||||
<meta property="og:description" content="Description Here">
|
|
||||||
<meta property="og:site_name" content="Site Name">
|
|
||||||
<meta property="og:locale" content="en_US">
|
|
||||||
<meta property="article:author" content="">
|
|
26
src/resources/views/meta/opengraph.blade.php
Normal file
26
src/resources/views/meta/opengraph.blade.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<!-- https://ogp.me/ -->
|
||||||
|
<meta property="og:site_name" content="Site Name">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://example.com/page.html">
|
||||||
|
<meta property="og:locale" content="en_US">
|
||||||
|
<meta property="og:title" content="Content Title">
|
||||||
|
<meta property="og:description" content="Description Here">
|
||||||
|
|
||||||
|
<meta property="og:image" content="https://example.com/image.jpg">
|
||||||
|
<meta property="og:image:type" content="image/jpeg">
|
||||||
|
<meta property="og:image:width" content="640">
|
||||||
|
<meta property="og:image:height" content="480">
|
||||||
|
<meta property="og:image:alt" content="A description of what is in the image (not a caption)">
|
||||||
|
|
||||||
|
<meta property="og:video" content="https://example.com/video.m4v">
|
||||||
|
<meta property="og:video:type" content="video/mp4">
|
||||||
|
<meta property="og:video:width" content="640">
|
||||||
|
<meta property="og:video:height" content="480">
|
||||||
|
|
||||||
|
<meta property="og:audio" content="https://example.com/sound.mp3">
|
||||||
|
<meta property="og:audio:type" content="audio/mpeg">
|
||||||
|
|
||||||
|
<meta property="article:author" content="">
|
||||||
|
<meta property="article:published_time" content="YYYY-MM-DDTHH:MM:SS+00:00">
|
||||||
|
<meta property="article:modified_time" content="YYYY-MM-DDTHH:MM:SS+00:00">
|
||||||
|
<meta property="article:expiration_time" content="YYYY-MM-DDTHH:MM:SS+00:00">
|
@ -61,6 +61,19 @@ module.exports = {
|
|||||||
900: '#8d0f11',
|
900: '#8d0f11',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'totem-pole': {
|
||||||
|
50: '#fff0f0',
|
||||||
|
100: '#ffdddd',
|
||||||
|
200: '#ffc0c0',
|
||||||
|
300: '#ff9494',
|
||||||
|
400: '#ff5757',
|
||||||
|
500: '#ff2323',
|
||||||
|
600: '#ff0303',
|
||||||
|
700: '#d70000',
|
||||||
|
800: '#b10303',
|
||||||
|
900: '#8f0a0a',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// oranges
|
// oranges
|
||||||
|
|
||||||
@ -134,6 +147,20 @@ module.exports = {
|
|||||||
900: '#1a4d31',
|
900: '#1a4d31',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'japanese-laurel': {
|
||||||
|
50: '#ebffe5',
|
||||||
|
100: '#d0ffc7',
|
||||||
|
200: '#a4ff95',
|
||||||
|
300: '#6bfe58',
|
||||||
|
400: '#3cf526',
|
||||||
|
500: '#18dc06',
|
||||||
|
600: '#0cad00',
|
||||||
|
700: '#0c8506',
|
||||||
|
800: '#0f690b',
|
||||||
|
900: '#10580f',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// teals
|
// teals
|
||||||
'aquamarine': {
|
'aquamarine': {
|
||||||
50: '#eafff7',
|
50: '#eafff7',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user