Initial commit: OAuth IAM Laravel application

Laravel-based OAuth/IAM service with Passport, Vue 3 frontend, and
Docker Compose setup for local and production deployment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 01:02:41 +02:00
commit c2b5291a74
174 changed files with 29318 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.app-nav {
position: sticky;
top: 0;
z-index: 20;
border-bottom: 1px solid var(--color-border);
background: rgb(255 255 255 / 80%);
backdrop-filter: blur(8px);
@include dark {
background: rgb(9 9 11 / 80%);
}
}
.app-nav__inner {
max-width: $page-max-width;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: $space-4;
padding: $space-3 $space-6;
}
.app-nav__section {
display: flex;
align-items: center;
gap: $space-6;
}
.app-nav__brand {
display: flex;
align-items: center;
gap: $space-2;
}
.app-nav__logo {
display: grid;
place-items: center;
height: 1.75rem;
width: 1.75rem;
border-radius: $radius-md;
background: var(--gradient-accent);
color: #fff;
font-size: 0.7rem;
font-weight: 700;
}
.app-nav__brand-name {
font-weight: 600;
letter-spacing: -0.01em;
color: var(--color-text-primary);
}
.app-nav__links {
display: none;
align-items: center;
gap: $space-1;
@include respond-up($breakpoint-md) {
display: flex;
}
}
.app-nav__link {
border-radius: $radius-full;
padding: $space-1 * 1.5 $space-3;
font-size: $font-sm;
font-weight: 500;
color: var(--color-text-secondary);
transition: $transition-fast;
&:hover {
background: var(--color-surface-muted);
color: var(--color-text-primary);
}
&--active {
background: var(--color-accent-soft);
color: var(--color-accent-soft-text);
}
}
.app-nav__controls {
display: flex;
align-items: center;
gap: $space-2;
}
.app-nav__theme-toggle {
display: grid;
place-items: center;
height: 2rem;
width: 2rem;
border-radius: $radius-full;
border: 1px solid var(--color-border-strong);
color: var(--color-text-secondary);
transition: $transition-fast;
@include dark {
border-color: var(--color-border);
}
&:hover {
background: var(--color-surface-muted);
color: var(--color-text-primary);
}
svg {
height: 1rem;
width: 1rem;
}
}
.app-nav__user {
display: none;
font-size: $font-sm;
color: var(--color-text-secondary);
@include respond-up($breakpoint-sm) {
display: inline;
}
}
.page {
max-width: $page-max-width;
margin: 0 auto;
padding: $space-10 $space-6;
}

View File

@@ -0,0 +1,130 @@
@use '../abstracts/variables' as *;
.auth-page {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
padding: 0 $space-4;
background: var(--color-page-bg);
}
.auth-page__glow {
pointer-events: none;
position: absolute;
top: -8rem;
left: 50%;
transform: translateX(-50%);
height: 20rem;
width: 36rem;
border-radius: $radius-full;
background: var(--gradient-glow);
filter: blur(64px);
}
.auth-card {
position: relative;
width: 100%;
max-width: $auth-card-width;
border-radius: $radius-2xl;
border: 1px solid var(--color-border);
background: var(--color-surface);
padding: $space-8;
box-shadow: var(--shadow-elevated);
}
.auth-card--wide {
max-width: 28rem;
}
.auth-card__logo {
display: grid;
place-items: center;
height: 2.25rem;
width: 2.25rem;
border-radius: $radius-lg;
background: var(--gradient-accent);
color: #fff;
font-size: $font-xs;
font-weight: 700;
box-shadow: 0 1px 2px 0 rgb(79 70 229 / 20%);
}
.auth-card__title {
margin-top: $space-4;
font-size: $font-xl;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--color-text-primary);
}
.auth-card__subtitle {
margin-top: $space-1;
font-size: $font-sm;
color: var(--color-text-secondary);
}
.auth-card__form {
margin-top: $space-6;
display: flex;
flex-direction: column;
gap: $space-4;
}
.auth-card__footer {
margin-top: $space-5;
text-align: center;
font-size: $font-sm;
color: var(--color-text-secondary);
}
.auth-card__banner {
margin-top: $space-4;
border-radius: $radius-xl;
border: 1px solid var(--color-border);
background: var(--color-surface-muted);
padding: $space-3;
font-size: $font-sm;
color: var(--color-text-secondary);
}
.auth-card__hint {
margin-top: $space-2;
text-align: center;
font-size: $font-xs;
color: var(--color-text-muted);
}
.auth-card__scopes {
margin-top: $space-4;
display: flex;
flex-direction: column;
gap: $space-2;
border-radius: $radius-xl;
border: 1px solid var(--color-border);
background: var(--color-surface-muted);
padding: $space-3;
}
.auth-card__scope {
display: flex;
gap: $space-2;
font-size: $font-sm;
color: var(--color-text-secondary);
}
.auth-card__scope-check {
color: var(--color-accent-strong);
}
.auth-card__actions {
margin-top: $space-6;
display: flex;
gap: $space-3;
form {
flex: 1;
}
}

View File

@@ -0,0 +1,24 @@
@use '../abstracts/variables' as *;
.badge {
display: inline-flex;
align-items: center;
gap: $space-1 * 1.5;
font-size: $font-xs;
font-weight: 500;
}
.badge__dot {
width: 0.375rem;
height: 0.375rem;
border-radius: $radius-full;
background: currentColor;
}
.badge--active {
color: var(--color-success);
}
.badge--inactive {
color: var(--color-text-muted);
}

View File

@@ -0,0 +1,36 @@
@use '../abstracts/variables' as *;
.banner {
margin-top: $space-6;
border-radius: $radius-xl;
padding: $space-4;
font-size: $font-sm;
}
.banner--warning {
border: 1px solid var(--color-warning-border);
background: var(--color-warning-bg);
}
.banner__title {
font-weight: 500;
color: var(--color-warning-title);
}
.banner__line {
margin-top: $space-1;
color: var(--color-warning-text);
}
.banner__code {
background: var(--color-warning-code-bg);
border-radius: $radius-sm;
padding: 0 $space-1;
}
.banner__close {
margin-top: $space-2;
color: var(--color-warning-title);
text-decoration: underline;
font-size: $font-sm;
}

View File

@@ -0,0 +1,68 @@
@use '../abstracts/variables' as *;
.bar-list {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: $radius-xl;
padding: $space-5;
}
.bar-list__title {
font-size: $font-sm;
font-weight: 500;
color: var(--color-text-primary);
}
.bar-list__empty {
margin-top: $space-3;
font-size: $font-sm;
color: var(--color-text-muted);
}
.bar-list__items {
margin-top: $space-4;
display: flex;
flex-direction: column;
gap: $space-3;
}
.bar-list__row-head {
margin-bottom: $space-1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: $font-xs;
}
.bar-list__label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--color-text-secondary);
}
.bar-list__value {
margin-left: $space-2;
flex-shrink: 0;
color: var(--color-text-secondary);
font-variant-numeric: tabular-nums;
}
.bar-list__track {
height: 0.875rem;
width: 100%;
border-radius: $radius-full;
background: var(--color-surface-muted);
}
.bar-list__fill {
height: 0.875rem;
border-radius: 0 $radius-full $radius-full 0;
background: var(--series-1, var(--color-accent));
transition: filter 100ms;
}
.bar-list__row:hover .bar-list__fill,
.bar-list__row:focus-within .bar-list__fill {
filter: brightness(1.1);
}

View File

@@ -0,0 +1,107 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: $space-2;
padding: $space-2 $space-4;
border-radius: $radius-lg;
font-size: $font-sm;
font-weight: 500;
transition: $transition-fast;
white-space: nowrap;
@include focus-ring;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&--full {
width: 100%;
}
&--pill {
border-radius: $radius-full;
}
}
.btn--primary {
background: var(--gradient-accent);
color: var(--color-text-on-accent);
box-shadow: 0 1px 2px 0 rgb(79 70 229 / 20%);
&:hover:not(:disabled) {
opacity: 0.9;
}
}
// Variante sólida (no-gradiente) del acento, para acciones dentro de tarjetas
// donde el gradiente sería demasiado.
.btn--accent {
background: var(--color-accent);
color: var(--color-text-on-accent);
border-radius: $radius-md;
padding: $space-2 $space-4;
&:hover:not(:disabled) {
background: var(--color-accent-strong);
}
}
.btn--secondary {
background: transparent;
border: 1px solid var(--color-border-strong);
color: var(--color-text-primary);
@include dark {
border-color: var(--color-border);
}
&:hover:not(:disabled) {
background: var(--color-surface-muted);
}
}
// Botón oscuro/claro invertido (usado para "Salir" en la nav).
.btn--invert {
background: #111827;
color: #ffffff;
border-radius: $radius-full;
&:hover:not(:disabled) {
background: #374151;
}
@include dark {
background: #ffffff;
color: #111827;
&:hover:not(:disabled) {
background: #e5e7eb;
}
}
}
// Botón oscuro sólido (asignar rol dentro de Usuarios).
.btn--dark {
background: #111827;
color: #ffffff;
border-radius: $radius-md;
&:hover:not(:disabled) {
background: #374151;
}
@include dark {
background: #ffffff;
color: #111827;
&:hover:not(:disabled) {
background: #e5e7eb;
}
}
}

View File

@@ -0,0 +1,35 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: $radius-xl;
padding: $space-5;
}
.card__title {
font-weight: 500;
color: var(--color-text-primary);
}
.card__subtitle {
margin-top: $space-1;
font-size: $font-sm;
color: var(--color-text-secondary);
}
// Tarjetas-link del dashboard: hover con leve elevación.
.card--interactive {
display: block;
transition:
transform $transition-fast,
border-color $transition-fast,
box-shadow $transition-fast;
&:hover {
transform: translateY(-2px);
border-color: var(--color-accent-strong);
box-shadow: var(--shadow-elevated);
}
}

View File

@@ -0,0 +1,21 @@
@use '../abstracts/variables' as *;
.chip {
display: inline-flex;
align-items: center;
gap: $space-1;
background: var(--color-accent-soft);
color: var(--color-accent-soft-text);
border-radius: $radius-full;
padding: $space-1 $space-2 * 1.25;
font-size: $font-xs;
}
.chip__remove {
color: var(--color-accent-strong);
opacity: 0.6;
&:hover {
opacity: 1;
}
}

View File

@@ -0,0 +1,80 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.field {
display: flex;
flex-direction: column;
}
.field__label {
font-size: $font-sm;
font-weight: 500;
color: var(--color-text-primary);
margin-bottom: $space-1;
}
.field__hint {
margin-top: $space-1;
font-size: $font-xs;
color: var(--color-text-muted);
}
.field__error {
margin-top: $space-1;
font-size: $font-sm;
color: var(--color-danger);
}
.field__input,
.field__select {
width: 100%;
border: 1px solid var(--color-border-strong);
background: var(--color-surface);
color: var(--color-text-primary);
border-radius: $radius-md;
padding: $space-2 $space-3;
font-size: $font-sm;
transition: border-color $transition-fast;
@include dark {
border-color: var(--color-border);
background: var(--color-surface-muted);
}
&::placeholder {
color: var(--color-text-muted);
}
&:focus {
outline: none;
border-color: var(--color-accent-strong);
box-shadow: 0 0 0 3px rgb(99 102 241 / 15%);
}
}
.field--sm {
.field__label {
font-size: $font-xs;
color: var(--color-text-secondary);
}
.field__input,
.field__select {
padding: $space-1 * 1.5 $space-2;
}
}
.field__checkbox-row {
display: flex;
align-items: center;
gap: $space-2;
font-size: $font-sm;
color: var(--color-text-secondary);
}
.field__checkbox {
width: 1rem;
height: 1rem;
border-radius: $radius-sm;
accent-color: var(--color-accent);
}

View File

@@ -0,0 +1,28 @@
@use '../abstracts/variables' as *;
.link {
color: var(--color-accent-strong);
font-weight: 500;
font-size: inherit;
&:hover {
text-decoration: underline;
}
}
.link--danger {
color: var(--color-danger);
font-weight: 500;
&:hover {
text-decoration: underline;
}
}
.link--muted {
color: var(--color-text-secondary);
&:hover {
color: var(--color-text-primary);
}
}

View File

@@ -0,0 +1,20 @@
@use '../abstracts/variables' as *;
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: $space-4;
}
.page-title {
font-size: $font-2xl;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--color-text-primary);
}
.page-subtitle {
margin-top: $space-1;
color: var(--color-text-secondary);
}

View File

@@ -0,0 +1,18 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.pill-select {
border: 1px solid var(--color-border-strong);
background: var(--color-surface);
color: var(--color-text-secondary);
border-radius: $radius-full;
padding: $space-1 * 1.5 $space-3;
font-size: $font-sm;
font-weight: 500;
@include dark {
border-color: var(--color-border);
background: var(--color-surface-muted);
color: var(--color-text-secondary);
}
}

View File

@@ -0,0 +1,26 @@
@use '../abstracts/variables' as *;
.stat-tile {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: $radius-xl;
padding: $space-4;
}
.stat-tile__label {
font-size: $font-xs;
color: var(--color-text-secondary);
}
.stat-tile__value {
margin-top: $space-1;
font-size: $font-2xl;
font-weight: 600;
color: var(--color-text-primary);
}
.stat-tile__hint {
margin-top: $space-1;
font-size: $font-xs;
color: var(--color-text-muted);
}

View File

@@ -0,0 +1,62 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.table-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: $radius-xl;
overflow-x: auto;
}
.table {
width: 100%;
font-size: $font-sm;
}
.table__head {
background: var(--color-surface-muted);
}
.table__head-cell {
text-align: left;
font-weight: 500;
color: var(--color-text-secondary);
padding: $space-2 $space-4;
}
.table__body {
tr {
border-top: 1px solid var(--color-border);
&:hover {
background: var(--color-surface-muted);
}
}
tr.table__row--edit:hover {
background: none;
}
}
.table__row--edit {
background: var(--color-surface-muted);
}
.table__cell {
padding: $space-2 $space-4;
color: var(--color-text-secondary);
&--primary {
color: var(--color-text-primary);
}
&--actions {
text-align: right;
}
}
.table__empty {
padding: $space-4;
font-size: $font-sm;
color: var(--color-text-secondary);
}