Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions ui/src/components/CompanyEditPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
UI; the dialog is informational. The lock status + member
count are surfaced as a read-only chip / line item so the
view actually shows MORE than the form does, not less. -->
<v-card flat>
<v-card flat class="cep">
<v-alert v-if="demoMode" type="info" variant="tonal" density="compact" class="ma-4">
{{ t('company.demoEdit') }}
</v-alert>
Expand Down Expand Up @@ -79,22 +79,16 @@
</v-form>
</v-card-text>

<v-card-actions v-if="!loading">
<v-btn v-if="isEdit" color="error" variant="tonal" :disabled="saving" @click="confirmDelete = true">
<v-icon start>mdi-delete</v-icon> {{ t('common.delete') }}
</v-btn>
<v-spacer />
<!-- View mode: a single "Close" button is the only sane action
when nothing is editable. Create mode keeps the usual
Cancel + Save pair so the form validation has a submit
target. -->
<v-btn variant="text" :disabled="saving" @click="emit('cancel')">
{{ isEdit ? t('common.close') : t('common.cancel') }}
</v-btn>
<v-btn v-if="!isEdit" color="primary" variant="elevated" :loading="saving" @click="save">
<v-icon start>mdi-content-save</v-icon> {{ t('common.save') }}
</v-btn>
</v-card-actions>
<div v-if="!loading" class="cep-foot">
<button v-if="isEdit" class="mbtn ghost-danger" :disabled="saving" @click="confirmDelete = true">
<v-icon size="18">mdi-delete</v-icon>{{ t('common.delete') }}
</button>
<span class="foot-sp" />
<button class="mbtn ghost" :disabled="saving" @click="emit('cancel')">{{ isEdit ? t('common.close') : t('common.cancel') }}</button>
<button v-if="!isEdit" class="mbtn primary" :disabled="saving" @click="save">
<span v-if="saving" class="mspin" aria-hidden="true" /><v-icon v-else size="18">mdi-content-save</v-icon>{{ t('common.save') }}
</button>
</div>

<LigojConfirmDialog
v-model="confirmDelete"
Expand All @@ -112,8 +106,9 @@

<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useApi, useErrorStore, useI18nStore, LigojConfirmDialog } from '@ligoj/host'
import { useApi, useErrorStore, useI18nStore } from '@ligoj/host'
import { TYPE_ICONS } from '../composables/delegateTypes.js'
import LigojConfirmDialog from '@/components/VibrantConfirmDialog.vue'

const props = defineProps({
/**
Expand Down Expand Up @@ -309,3 +304,30 @@ async function remove() {
opacity: 0.7;
}
</style>

<style scoped>
.cep {
--ink-2: rgba(var(--v-theme-on-surface), .72);
--border: rgba(var(--v-theme-on-surface), .14);
--border-2: rgba(var(--v-theme-on-surface), .26);
--hover: rgba(var(--v-theme-on-surface), .06);
--font: var(--v26-font, "Bricolage Grotesque", system-ui, sans-serif);
background: transparent !important;
}
.cep :deep(.v-card-text) { padding: 4px 24px 4px !important; }
.cep :deep(.v-field) { border-radius: 12px; font-family: var(--font); }
.cep :deep(.v-field__prepend-inner .v-icon) { opacity: .55; }
.cep :deep(.v-label) { font-weight: 600; }
.cep-foot { display: flex; align-items: center; gap: 10px; padding: 12px 24px 22px; }
.foot-sp { flex: 1; }
.mbtn { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font); font-weight: 700; font-size: 14px; padding: 10px 17px; border-radius: 12px; cursor: pointer; border: 1px solid transparent; transition: filter .15s, background .15s, border-color .15s; }
.mbtn.primary { color: #fff; background: linear-gradient(135deg, #ff9436, #ff5a52); box-shadow: 0 8px 18px -10px rgba(255, 90, 82, .55); }
.mbtn.primary:hover:not(:disabled) { filter: brightness(1.04); }
.mbtn.ghost { color: var(--ink-2); background: transparent; border-color: var(--border); }
.mbtn.ghost:hover:not(:disabled) { background: var(--hover); border-color: var(--border-2); }
.mbtn.ghost-danger { color: rgb(var(--v-theme-error)); background: transparent; border-color: rgba(var(--v-theme-error), .35); }
.mbtn.ghost-danger:hover:not(:disabled) { background: rgba(var(--v-theme-error), .08); }
.mbtn:disabled { opacity: .6; cursor: default; }
.mspin { width: 15px; height: 15px; border: 2px solid rgba(255, 255, 255, .5); border-top-color: #fff; border-radius: 50%; animation: cepspin .7s linear infinite; }
@keyframes cepspin { to { transform: rotate(360deg); } }
</style>
62 changes: 44 additions & 18 deletions ui/src/components/GroupEditPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
emits events so the parent can react (close the dialog, refresh
the table, …). No router awareness — receives the editing
target through the `groupId` prop (null = new). -->
<v-card flat>
<v-card flat class="gp">
<v-alert v-if="demoMode" type="info" variant="tonal" density="compact" class="ma-4">
{{ t('group.demoEdit') }}
</v-alert>
Expand Down Expand Up @@ -55,22 +55,16 @@
</v-form>
</v-card-text>

<v-card-actions v-if="!loading">
<v-btn v-if="isEdit" color="error" variant="tonal" :disabled="saving" @click="confirmDelete = true">
<v-icon start>mdi-delete</v-icon> {{ t('common.delete') }}
</v-btn>
<v-spacer />
<!-- View mode: a single "Close" button reads as the only sane
action when nothing is editable. Create mode keeps the
usual Cancel + Save pair so the form validation has a
submit target. -->
<v-btn variant="text" :disabled="saving" @click="emit('cancel')">
{{ isEdit ? t('common.close') : t('common.cancel') }}
</v-btn>
<v-btn v-if="!isEdit" color="primary" variant="elevated" :loading="saving" @click="save">
<v-icon start>mdi-content-save</v-icon> {{ t('common.save') }}
</v-btn>
</v-card-actions>
<div v-if="!loading" class="gp-foot">
<button v-if="isEdit" class="mbtn ghost-danger" :disabled="saving" @click="confirmDelete = true">
<v-icon size="18">mdi-delete</v-icon>{{ t('common.delete') }}
</button>
<span class="foot-sp" />
<button class="mbtn ghost" :disabled="saving" @click="emit('cancel')">{{ isEdit ? t('common.close') : t('common.cancel') }}</button>
<button v-if="!isEdit" class="mbtn primary" :disabled="saving" @click="save">
<span v-if="saving" class="mspin" aria-hidden="true" /><v-icon v-else size="18">mdi-content-save</v-icon>{{ t('common.save') }}
</button>
</div>

<LigojConfirmDialog
v-model="confirmDelete"
Expand All @@ -88,8 +82,9 @@

<script setup>
import { ref, computed, onMounted } from 'vue'
import { useApi, useErrorStore, useI18nStore, LigojConfirmDialog } from '@ligoj/host'
import { useApi, useErrorStore, useI18nStore } from '@ligoj/host'
import { TYPE_ICONS } from '../composables/delegateTypes.js'
import LigojConfirmDialog from '@/components/VibrantConfirmDialog.vue'

const props = defineProps({
/**
Expand Down Expand Up @@ -288,3 +283,34 @@ async function remove() {
}
}
</script>

<style scoped>
.gp {
--ink: rgb(var(--v-theme-on-surface));
--ink-2: rgba(var(--v-theme-on-surface), .72);
--border: rgba(var(--v-theme-on-surface), .14);
--border-2: rgba(var(--v-theme-on-surface), .26);
--hover: rgba(var(--v-theme-on-surface), .06);
--font: var(--v26-font, "Bricolage Grotesque", system-ui, sans-serif);
background: transparent !important;
}
.gp :deep(.v-card-text) { padding: 4px 24px 4px !important; }
/* Clean rounded fields (match the user dialog). */
.gp :deep(.v-field) { border-radius: 12px; font-family: var(--font); }
.gp :deep(.v-field__prepend-inner .v-icon) { opacity: .55; }
.gp :deep(.v-label) { font-weight: 600; }

/* Vibrant footer buttons (shared language with UserEditDialog). */
.gp-foot { display: flex; align-items: center; gap: 10px; padding: 12px 24px 22px; }
.foot-sp { flex: 1; }
.mbtn { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font); font-weight: 700; font-size: 14px; padding: 10px 17px; border-radius: 12px; cursor: pointer; border: 1px solid transparent; transition: filter .15s, background .15s, border-color .15s; }
.mbtn.primary { color: #fff; background: linear-gradient(135deg, #ff9436, #ff5a52); box-shadow: 0 8px 18px -10px rgba(255, 90, 82, .55); }
.mbtn.primary:hover:not(:disabled) { filter: brightness(1.04); }
.mbtn.ghost { color: var(--ink-2); background: transparent; border-color: var(--border); }
.mbtn.ghost:hover:not(:disabled) { background: var(--hover); border-color: var(--border-2); }
.mbtn.ghost-danger { color: rgb(var(--v-theme-error)); background: transparent; border-color: rgba(var(--v-theme-error), .35); }
.mbtn.ghost-danger:hover:not(:disabled) { background: rgba(var(--v-theme-error), .08); }
.mbtn:disabled { opacity: .6; cursor: default; }
.mspin { width: 15px; height: 15px; border: 2px solid rgba(255, 255, 255, .5); border-top-color: #fff; border-radius: 50%; animation: gpspin .7s linear infinite; }
@keyframes gpspin { to { transform: rotate(360deg); } }
</style>
32 changes: 18 additions & 14 deletions ui/src/components/GroupMembersDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,13 @@
— GroupListView's row action and the host's subscription-row
buttons (returned by `service.renderFeatures`) both call it. -->
<v-dialog v-model="open" max-width="1100" scrollable @after-leave="onAfterLeave">
<v-card>
<v-card-title class="d-flex align-center ga-2">
<v-icon color="primary">mdi-account-group</v-icon>
<!-- "Group members — <name>" reads as the action being done.
The group name is the variable bit so we keep it in a
distinct span the user's eye lands on first. -->
<span>{{ t('id.group.manageTitle') }}</span>
<span class="text-primary">{{ groupName }}</span>
<v-spacer />
<v-btn icon size="small" variant="text" @click="onCloseClick">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="pa-4">
<v-card class="vmodal">
<div class="vmodal-head">
<span class="mi"><v-icon color="#fff">mdi-account-multiple</v-icon></span>
<h3>{{ t('id.group.manageTitle') }} <span class="who">{{ groupName }}</span></h3>
<button class="x" :aria-label="t('common.cancel')" @click="onCloseClick"><v-icon size="20">mdi-close</v-icon></button>
</div>
<v-card-text class="vmodal-body">
<!-- :key forces the Panel to remount when the group changes,
so its internal data-table state (pagination, search,
selection) starts fresh for each opened group. The
Expand Down Expand Up @@ -94,3 +87,14 @@ function onAfterLeave() {
pendingOnChanged = null
}
</script>

<style scoped>
.vmodal { border-radius: 20px !important; box-shadow: 0 30px 80px -30px rgba(0, 0, 0, .55) !important; }
.vmodal-head { display: flex; align-items: center; gap: 13px; padding: 22px 24px 8px; }
.vmodal-head .mi { width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; flex: none; background: linear-gradient(135deg, #ff9436, #ff5a52); box-shadow: 0 8px 18px -8px rgba(255, 90, 82, .6); }
.vmodal-head h3 { font-family: var(--v26-font, "Bricolage Grotesque", system-ui, sans-serif); font-weight: 800; font-size: 20px; margin: 0; flex: 1; color: rgb(var(--v-theme-on-surface)); letter-spacing: -.02em; }
.vmodal-head h3 .who { color: #ff5a52; }
.vmodal-head .x { width: 36px; height: 36px; border: 0; background: transparent; border-radius: 9px; cursor: pointer; display: grid; place-items: center; color: rgba(var(--v-theme-on-surface), .5); }
.vmodal-head .x:hover { background: rgba(var(--v-theme-on-surface), .06); color: rgb(var(--v-theme-on-surface)); }
.vmodal-body { padding: 14px 24px 20px !important; }
</style>
Loading
Loading