diff --git a/assets/css/scss/atoms/_autocomplete.scss b/assets/css/scss/atoms/_autocomplete.scss index 0128bc91576..a9a856029bc 100644 --- a/assets/css/scss/atoms/_autocomplete.scss +++ b/assets/css/scss/atoms/_autocomplete.scss @@ -1,130 +1,139 @@ .p-autocomplete { - @apply flex; + @apply inline-flex; &-loader { @apply absolute top-1/2 -mt-2 right-3; } - &-dd &-input { + &:has(&-dropdown) &-loader { + @apply pr-7; + } + + &:has(&-dropdown) &-input { @apply flex-auto w-[1%]; } - &-dd &-input, - &-dd &-multiple-container { + &:has(&-dropdown) &-input, + &:has(&-dropdown) &-input-multiple { @apply rounded-r-lg; } - &-dd &-dropdown { - @apply rounded-l-lg; + &-dropdown { + @apply cursor-pointer inline-flex select-none items-center justify-center overflow-hidden relative w-full bg-white transition-colors outline-none rounded-l-lg + focus-visible:outline-none; + + &:not(:disabled) { + @apply + hover:bg-support-1 + active:bg-support-1 + focus:bg-support-1; + } } - & &-panel { + & &-overlay { @apply min-w-full; } - &-panel { - @apply absolute overflow-auto top-0 left-0 bg-white drop-shadow-lg rounded-lg border-none text-gray-90 + &-overlay { + @apply absolute overflow-auto top-0 left-0 bg-white drop-shadow-lg rounded-lg border-none text-gray-90 mt-1 before:content-[""] before:w-full before:block before:h-3 before:sticky before:bg-white before:z-[2] before:top-0 before:rounded-t-lg before:rounded-b-none after:content-[""] after:w-full after:block after:h-3 after:sticky after:bg-white after:z-[2] after:bottom-0 after:rounded-b-lg after:rounded-t-none; + } - .p-autocomplete-items { - @apply p-0; - - .p-autocomplete-item { - @apply m-0 px-4 py-2 transition text-body-2 outline-0 outline-none text-gray-90 border-none bg-transparent rounded-none; + &-list-container { + @apply overflow-auto; + } - &:first-child { - @apply mt-0; - } + &-list { + @apply m-0 p-0 list-none flex flex-col gap-0; + } - &:last-child { - @apply mb-0; - } + &-option { + @apply cursor-pointer whitespace-nowrap relative overflow-hidden flex items-center border-0 text-gray-90 text-body-2 transition-colors px-4 py-2; + } - &.p-highlight { - @apply bg-support-3 text-primary; - } + &-option:not(&-option-selected):not(.p-disabled).p-focus { + @apply text-primary bg-white; + } - &:not(.p-highlight):not(.p-disabled).p-focus { - @apply text-primary; - } + &-option-selected { + @apply text-primary bg-support-1; - &-group { - @apply m-0 px-3 py-4 bg-white text-gray-90; - } - } + &.p-focus { + @apply text-primary bg-support-1; } } - &-items { - @apply m-0 p-0 list-none; + &-option-group { + @apply m-0 px-3 py-4 bg-white text-gray-90; } - &-item { - @apply cursor-pointer whitespace-nowrap relative overflow-hidden; + &-input-multiple { + @apply m-0 px-3 py-2 list-none cursor-text overflow-hidden flex items-center flex-wrap gap-2 outline-transparent transition-colors w-full border border-solid border-gray-50 rounded-lg; } - &-multiple-container { - @apply m-0 p-3 list-none cursor-text overflow-hidden flex items-center flex-wrap gap-2 outline-transparent; + &:not(.p-disabled):hover &-input-multiple { + @apply border-primary; + } - .p-autocomplete-input-token { - @apply p-0; + &:not(.p-disabled).p-focus &-input-multiple { + @apply border-primary outline-none; + } - input { - @apply p-0 m-0 text-gray-90; - } - } + &.p-invalid &-input-multiple { + @apply border-danger; + } - .p-autocomplete-token { - @apply px-1.5 py-0 rounded-lg text-gray-90 bg-gray-20; + &.p-disabled &-input-multiple { + @apply opacity-100 border-gray-50 text-gray-50; + } - .p-autocomplete-token-icon { - @apply ml-2; - } + &-chip.p-chip { - &.p-focus { - @apply text-primary; - } - } } - &-token { - @apply cursor-default inline-flex items-center flex-initial; + &-input-multiple:has(&-chip) { - &-icon { - @apply cursor-pointer inline-flex; - } } - &-input-token { - @apply flex-1 inline-flex; + &-chip-item.p-focus &-chip { + @apply p-0; + } + + &-input-chip { + @apply flex-auto inline-flex p-0; input { - @apply border-none outline-none bg-transparent m-0 p-0 shadow-none rounded-none w-full; + @apply border-none outline-none bg-transparent m-0 p-0 shadow-none rounded-none w-full + placeholder:text-gray-50; } } - &.p-autocomplete-dd { - &.p-autocomplete-loader { - @apply right-[3.75rem]; - } + &.p-invalid &-input-chip input { + @apply placeholder:text-danger; } - &:not(.p-disabled) { - &:hover { - .p-autocomplete-multiple-container { - @apply border-primary; - } - } + &-empty-message { + @apply px-4 py-2; + } + + &-fluid { + @apply flex; + } + + &-fluid:has(&-dropdown) &-input { + @apply w-[1%]; + } + + &:has(.p-inputtext-sm) &-dropdown { + .p-icon { - &.p-focus { - .p-autocomplete-multiple-container { - @apply outline-none outline-offset-0 border-primary; - } } } - &.p-invalid.p-component > .p-inputtext { - @apply border-error; + &:has(.p-inputtext-lg) &-dropdown { + .p-icon { + + } } } diff --git a/assets/css/scss/atoms/_calendar.scss b/assets/css/scss/atoms/_calendar.scss index fb3dd0a0e0f..42c47de1d2a 100644 --- a/assets/css/scss/atoms/_calendar.scss +++ b/assets/css/scss/atoms/_calendar.scss @@ -1,11 +1,191 @@ -.p-calendar { - @apply flex; +.p-datepicker { + @apply inline-flex max-w-full; - .p-inputtext { - @apply rounded-r-none; + &-input { + @apply flex-auto w-[1%]; } - .p-button { - @apply rounded-l-none; + &:has(&-dropdown) &-input { + @apply rounded-r-lg; + } + + &-dropdown { + @apply cursor-pointer inline-flex select-none items-center justify-center overflow-hidden relative w-full bg-white transition-colors outline-none rounded-l-lg + focus-visible:outline-none; + + &:not(:disabled) { + @apply + hover:bg-support-1 + active:bg-support-1 + focus:bg-support-1; + } + } + + &:has(&-input-icon-container) { + @apply relative; + } + + &:has(&-input-icon-container) &-input { + @apply pr-12; + } + + &-input-icon-container { + @apply cursor-pointer absolute top-3 right-4 text-gray-90; + + .p-icon { + @apply w-4 h-4 text-body-1 leading-none; + } + } + + &-fluid { + @apply flex; + } + + &-fluid &-input { + @apply w-[1%]; + } + + & &-panel { + @apply min-w-full; + } + + &-panel { + @apply w-auto absolute bg-white drop-shadow-lg rounded-lg border-none text-gray-90 mt-1 p-0 + before:content-[""] before:w-full before:block before:h-3 before:sticky before:bg-white before:z-[2] before:top-0 before:rounded-t-lg before:rounded-b-none + after:content-[""] after:w-full after:block after:h-3 after:sticky after:bg-white after:z-[2] after:bottom-0 after:rounded-b-lg after:rounded-t-none; + } + + &-panel-inline { + @apply inline-block overflow-x-auto drop-shadow-none; + } + + &-header { + @apply flex items-center justify-between p-0 text-gray-90; + } + + &-next-button { + @apply rtl:-order-1; + } + + &-prev-button { + @apply rtl:order-1; + } + + &-title { + @apply flex items-center justify-between gap-2 font-semibold; + } + + &-select-year, + &-select-month { + @apply border-none bg-transparent m-0 cursor-pointer transition-colors text-gray-90 px-2 py-0 + enabled:text-primary + hover:text-primary + focus-visible:text-primary focus-visible:outline-none; + } + + &-calendar-container { + @apply flex px-4; + } + + &-calendar-container &-calendar { + @apply flex-auto border-0 p-0; + } + + &-day-view { + @apply w-full border-collapse text-base m-0; + } + + &-weekday { + @apply font-semibold text-gray-90; + } + + &-day-cell { + @apply p-2; + } + + &-day { + @apply flex justify-center items-center cursor-pointer mx-auto my-0 overflow-hidden relative w-7 h-7 border-0 rounded-lg text-gray-90 text-body-2 transition-colors outline-none + focus-visible:outline-none; + + .p-datepicker-today > & { + @apply font-semibold; + } + + &-selected, + &-selected-range { + @apply bg-support-1 text-primary; + + .p-datepicker-today > & { + } + } + } + + &-day:not(.p-disabled):not(&-day-selected), + &-month:not(.p-disabled):not(&-month-selected), + &-year:not(.p-disabled):not(&-year-selected) { + @apply hover:bg-support-1; + } + + &-weeknumber { + @apply font-semibold text-secondary text-center; + } + + &-month-view, + &-year-view, + &-time-picker { + @apply mx-4; + } + + &-month, + &-year{ + @apply w-1/3 h-7 inline-flex items-center justify-center cursor-pointer overflow-hidden relative p-0 transition-colors rounded-lg outline-none text-gray-90 text-body-2; + + &-selected { + @apply bg-support-1 text-primary; + } + } + + &-month:not(.p-disabled), + &-year:not(.p-disabled) { + @apply focus-visible:outline-none; + } + + &-buttonbar { + @apply flex justify-between items-center p-2; + + .p-button { + @apply w-auto; + } + } + + &-time-picker { + @apply flex justify-center items-center gap-4; + + & > div { + @apply flex items-center flex-col gap-1; + } + + span { + @apply text-body-2; + } + + .p-timepicker-only & { + } + } + + &:has(.p-inputtext-sm) &-dropdown { + .p-icon { + } + } + + &:has(.p-inputtext-sm) &-input-icon { + } + + &:has(.p-inputtext-lg) &-dropdown { + .p-icon { + } + } + + &:has(.p-inputtext-lg) &-input-icon { } } diff --git a/assets/css/scss/atoms/_checkbox.scss b/assets/css/scss/atoms/_checkbox.scss index c38ee087cd4..4c0affd9b9d 100644 --- a/assets/css/scss/atoms/_checkbox.scss +++ b/assets/css/scss/atoms/_checkbox.scss @@ -1,53 +1,87 @@ .p-checkbox { - @apply relative inline-flex select-none align-bottom h-4 w-4; + @apply relative inline-flex select-none align-bottom w-4 h-4; &-input { - @apply cursor-pointer; + @apply cursor-pointer appearance-none absolute start-0 w-full h-full p-0 m-0 opacity-0 z-[1] outline-none outline-0 border border-transparent rounded-full; } &-box { - @apply flex justify-center items-center; - } + @apply flex justify-center items-center rounded border border-gray-50 w-4 h-4 transition outline-transparent; + + .p-checkbox-checked & { + @apply border-gray-50 bg-white; + } + + .p-checkbox-checked:not(.p-disabled):has(.p-checkbox-input:hover) & { + @apply border-primary bg-white; + } + + .p-checkbox:not(.p-disabled):has(.p-checkbox-input:focus-visible) & { + @apply border-primary drop-shadow-lg outline-none outline-0 + } - & &-input { - @apply appearance-none absolute top-0 left-0 w-full h-full p-0 m-0 opacity-0 z-[1] outline-none border-2 border-support-3 rounded; + .p-checkbox-checked:not(.p-disabled):has(.p-checkbox-input:focus-visible) & { + @apply border-primary; + } + + .p-checkbox.p-variant-filled & { } + + .p-checkbox-checked.p-variant-filled & {} + + .p-checkbox-checked.p-variant-filled:not(.p-disabled):has(.p-checkbox-input:hover) & { } } - & &-box { - @apply border-2 border-support-3 bg-white w-4 h-4 text-primary rounded transition-none outline-none; + &-icon { + @apply duration-200 text-gray-90 text-body-2 w-3 h-3; - .p-checkbox-icon { - @apply duration-200 text-white text-tiny; + .p-checkbox-checked & { + @apply text-gray-90; + } - &.p-icon { - @apply w-2.5 h-2.5; + .p-checkbox-checked:not(.p-disabled):has(.p-checkbox-input:hover) & { + @apply text-primary; + } + } + + &:not(.p-disabled) { + &:has(.p-checkbox-input:hover) { + .p-checkbox-box { + @apply border-primary; } } } - &:has(&-input--legacy:checked) &-box, - &.p-highlight &-box { - @apply border-primary bg-primary; + &.p-invalid > & { + @apply border-danger; } - &:not(.p-disabled):has(&-input:hover) { - .p-checkbox-box { - @apply border-primary; - } + &.p-disabled { + @apply opacity-100; + + & .p-checkbox-box { + @apply border-gray-50 bg-gray-20; - &:has(.p-checkbox-input--legacy:checked) .p-checkbox-box, - &.p-highlight .p-checkbox-box { - @apply border-primary bg-primary text-white; + .p-checkbox-icon { + @apply text-gray-50; + } } } - &:not(.p-disabled):has(&-input:focus-visible) { - .p-checkbox-box { - @apply outline-none drop-shadow-lg bg-primary; - } + &-sm, + &-sm &-box { + @apply w-2 h-2; + } + + &-sm &-icon { + @apply text-body-2 w-2 h-2; + } + + &-lg, + &-lg &-box { + @apply w-6 h-6; } - &.p-invalid > &-box { - @apply border-error; + &-lg &-icon { + @apply text-body-2 w-5 h-5; } } diff --git a/assets/css/scss/atoms/_dropdown.scss b/assets/css/scss/atoms/_dropdown.scss deleted file mode 100644 index 3bc7ebbac8e..00000000000 --- a/assets/css/scss/atoms/_dropdown.scss +++ /dev/null @@ -1,85 +0,0 @@ -.p-dropdown, .p-multiselect { - @apply inline-flex cursor-pointer relative select-none - border border-support-3 bg-white rounded-lg transition w-full duration-200 - hover:border-primary hover:text-gray-90 hover:outline-0 hover:outline-none - focus:border-primary focus:text-gray-90 focus:outline-0 focus:outline-none; - - font-size: 14px; - - .p-dropdown-panel, .p-multiselect-panel { - @apply min-w-full; - } - - &.p-invalid.p-component { - @apply border-error text-error; - } -} - -select.p-dropdown { - @apply px-4 py-2; -} - -.p-dropdown { - &-clear-icon { - @apply absolute top-1/2 -mt-2; - } - - & &-clear-icon { - @apply right-12; - } -} - -.p-dropdown-trigger { - @apply flex items-center justify-center flex-shrink-0 - bg-transparent rounded-r-lg w-9; -} - -.p-dropdown-label, .p-multiselect-label { - @apply block whitespace-nowrap overflow-hidden flex-auto w-[1%] overflow-ellipsis cursor-pointer - bg-transparent border-none - focus:outline-0 focus:outline-none; -} - -.p-dropdown-label-empty { - @apply overflow-hidden opacity-0; -} - -.p-dropdown-panel, .p-multiselect-panel { - @apply absolute min-w-full top-2 left-0 - bg-white rounded-lg text-gray-90 drop-shadow-lg; - - .p-dropdown-items { - @apply rounded-lg - before:content-[""] before:w-full before:block before:h-3 before:sticky before:bg-white before:z-[2] before:top-0 before:rounded-t-lg before:rounded-b-none - after:content-[""] after:w-full after:block after:h-3 after:sticky after:bg-white after:z-[2] after:bottom-0 after:rounded-b-lg after:rounded-t-none; - - .p-dropdown-item { - @apply px-4 py-2 transition text-body-2 outline-0 outline-none text-gray-90; - - &.p-highlight { - @apply bg-support-3 text-primary; - } - - &.p-focus { - @apply text-primary; - } - } - } -} - -.p-dropdown-items-wrapper { - @apply overflow-auto rounded-lg; -} - -.p-dropdown-item { - @apply cursor-pointer whitespace-nowrap relative overflow-hidden flex items-center; -} - -.p-dropdown-items { - @apply m-0 p-0 list-none; -} - -.p-multiselect-label-container { - @apply p-2; -} - diff --git a/assets/css/scss/atoms/_float_label.scss b/assets/css/scss/atoms/_float_label.scss new file mode 100644 index 00000000000..c58182421fe --- /dev/null +++ b/assets/css/scss/atoms/_float_label.scss @@ -0,0 +1,65 @@ +.p-floatlabel { + @apply block relative; + + label { + @apply absolute top-1/2 pointer-events-none leading-none left-4 text-gray-50 bg-transparent transition-all duration-200; + } + + &:has(.p-textarea) label { + } + + &:has(.p-inputicon:first-child) label { + } + + &:has(.p-invalid) label { + @apply text-danger; + } + + &:has(input:focus) label, + &:has(input.p-filled) label, + &:has(input:-webkit-autofill) label, + &:has(textarea:focus) label, + &:has(textarea.p-filled) label, + &:has(.p-inputwrapper-focus) label, + &:has(.p-inputwrapper-filled) label { + @apply top-0 left-2 text-caption px-1 bg-white text-primary z-[1]; + } + + &:has(input.p-filled) label, + &:has(textarea.p-filled) label, + &:has(.p-inputwrapper-filled) label { + } + + &:has(input:focus) label, + &:has(input:-webkit-autofill) label, + &:has(textarea:focus) label, + &:has(.p-inputwrapper-focus) label { + } + + &-in .p-inputtext, + &-in .p-textarea, + &-in .p-select-label, + &-in .p-multiselect-label, + &-in .p-autocomplete-input-multiple, + &-in .p-cascadeselect-label, + &-in .p-treeselect-label { + } + + &-in:has(input:focus) label, + &-in:has(input.p-filled) label, + &-in:has(input:-webkit-autofill) label, + &-in:has(textarea:focus) label, + &-in:has(textarea.p-filled) label, + &-in:has(.p-inputwrapper-focus) label, + &-in:has(.p-inputwrapper-filled) label { + } + + &-on:has(input:focus) label, + &-on:has(input.p-filled) label, + &-on:has(input:-webkit-autofill) label, + &-on:has(textarea:focus) label, + &-on:has(textarea.p-filled) label, + &-on:has(.p-inputwrapper-focus) label, + &-on:has(.p-inputwrapper-filled) label { + } +} diff --git a/assets/css/scss/atoms/_input_switch.scss b/assets/css/scss/atoms/_input_switch.scss index f32cc52f384..d0b69ea62eb 100644 --- a/assets/css/scss/atoms/_input_switch.scss +++ b/assets/css/scss/atoms/_input_switch.scss @@ -1,47 +1,51 @@ -.p-inputswitch { - @apply align-middle h-4 w-8; +.p-toggleswitch { + @apply inline-block w-8 h-4; - .p-inputswitch-input { - @apply appearance-none absolute top-0 left-0 w-full h-full p-0 m-0 opacity-0 z-[1] outline-0 outline-none rounded-full; + &-input { + @apply cursor-pointer appearance-none absolute top-0 start-0 w-full h-full p-0 m-0 opacity-0 z-[1] outline-0 outline-none rounded-full; } - .p-inputswitch-slider { - @apply bg-gray-30 rounded-full transition-none outline-transparent - before:bg-white before:duration-200 before:h-3 before:left-0.5 before:-mt-1.5 before:rounded-full before:w-3; - } + &-slider { + @apply cursor-pointer w-full h-full border-0 border-none bg-gray-30 transition rounded-full drop-shadow-none; - &.p-highlight .p-inputswitch-slider { - @apply bg-secondary - before:bg-white before:translate-x-4; - } + .p-toggleswitch.p-toggleswitch-checked & { + @apply bg-secondary; + } - &:not(.p-disabled) { - &:has(.p-inputswitch-input:hover) { - .p-inputswitch-slider { - @apply bg-gray-30; - } + .p-toggleswitch:not(.p-disabled):has(.p-toggleswitch-input:hover) & { + @apply bg-gray-50; + } - &.p-highlight .p-inputswitch-slider { - @apply bg-secondary; - } + .p-toggleswitch:not(.p-disabled):has(.p-toggleswitch-input:hover).p-toggleswitch-checked & { + @apply bg-secondary; } - &:has(.p-inputswitch-input:focus-visible) .p-inputswitch-slider { - @apply outline-0 outline-none outline-offset-0 shadow-lg; + .p-toggleswitch:not(.p-disabled):has(.p-toggleswitch-input:focus-visible) & { + @apply outline-none outline-0; } - } - &.p-invalid > .p-inputswitch-slider { - @apply border-error; + .p-toggleswitch.p-disabled & { } + + .p-toggleswitch.p-invalid > & { + @apply border border-solid border-danger; + } } -} -.p-inputswitch-input { - @apply cursor-pointer; -} + &-handle { + @apply absolute flex justify-center items-center bg-white text-gray-90 h-3 w-3 top-0.5 left-0.5 rounded-full transition-colors; -.p-inputswitch-slider { - @apply absolute cursor-pointer top-0 left-0 right-0 bottom-0 border border-transparent - before:absolute before:content-[""] before:top-1/2; -} + .p-toggleswitch.p-toggleswitch-checked & { + @apply translate-x-4; + } + + .p-toggleswitch:not(.p-disabled):has(.p-toggleswitch-input:hover) & { } + + .p-toggleswitch.p-disabled & { } + .p-toggleswitch:not(.p-disabled):has(.p-toggleswitch-input:hover).p-toggleswitch-checked & { } + } + + &.p-disabled { + @apply opacity-100; + } +} diff --git a/assets/css/scss/atoms/_input_text.scss b/assets/css/scss/atoms/_input_text.scss index 40786f248fc..5254686e693 100644 --- a/assets/css/scss/atoms/_input_text.scss +++ b/assets/css/scss/atoms/_input_text.scss @@ -1,78 +1,46 @@ .p-inputtext { - @apply border border-support-3 px-4 py-2 bg-white rounded-lg text-gray-90 transition w-full duration-200 appearance-none font-sans - placeholder:text-support-3 - hover:border-primary hover:text-gray-90 hover:outline-0 hover:ring-0 - focus:border-primary focus:text-gray-90 focus:outline-0 focus:ring-0; + @apply text-body-2 text-gray-90 bg-white px-3 py-2.5 border border-solid border-gray-50 transition-colors appearance-none rounded-lg outline-transparent w-full; - font-size: 14px; - - &.p-invalid.p-component { - @apply border-error - placeholder:text-error; + &:enabled { + @apply hover:border-primary + focus:border-primary focus:outline-0 focus:outline-none; } - &.p-inputtext-sm { - @apply px-3 py-1.5 text-body-2; - } + &.p-invalid { + @apply border-danger; - &.p-inputtext-lg { - @apply py-3; + &::placeholder { + @apply text-danger; + } } -} -.p-icon-field { - @apply relative; + &.p-variant-filled { + @apply bg-support-1; - > .p-input-icon { - @apply absolute top-1/2 -mt-2; - } -} + &:enabled { } -.p-icon-field-left { - > .p-input-icon:first-of-type { - @apply left-3 text-primary; + &.p-filled { + @apply text-primary; + } } - > .p-inputtext { - @apply pl-9; + &:disabled { + @apply border-gray-50 text-gray-50; } -} -.p-icon-field-right { - > .p-input-icon:first-of-type { - @apply right-3 text-primary; + &::placeholder { + @apply text-gray-50; } - > .p-inputtext { - @apply pr-9; + &-sm { + @apply px-3 py-1.5; } -} -.p-input-icon-right { - > i:last-of-type, - > svg:last-of-type { - @apply right-3; + &-lg { + @apply py-3; } - > .p-inputtext { - @apply pr-10; + &-fluid { + @apply w-full } } - -.p-inputnumber { - @apply inline-flex; -} - -.p-inputnumber-input { - @apply flex-auto; -} - -.p-inputtextarea { - &-resizable { - @apply overflow-hidden resize-none; - } - - .p-fluid & { - @apply w-full; - } -} \ No newline at end of file diff --git a/assets/css/scss/atoms/_messages.scss b/assets/css/scss/atoms/_messages.scss index bb15065ef76..cb76e2a826c 100644 --- a/assets/css/scss/atoms/_messages.scss +++ b/assets/css/scss/atoms/_messages.scss @@ -1,38 +1,88 @@ .p-message { - @apply mb-4 shadow-sm rounded-md border-0; + @apply rounded-md outline-none outline-0; - &-wrapper { - @apply flex items-center; // gap-3.5; - } - - & &-wrapper { - @apply py-3 px-4; + &-content { + @apply flex items-center gap-4 py-3 px-4 h-full; } &-icon { - @apply shrink-0; + @apply shrink-0 text-body-1 w-6 h-6; } - & &-icon { - @apply text-body-1 mr-2; + &-close-button { + @apply flex items-center justify-center shrink-0 ms-auto overflow-hidden relative w-8 h-8 rounded-full bg-transparent transition outline-none cursor-pointer select-none; - &:not(.p-message-close-icon) { - @apply w-6 h-6; + &:focus-visible { + @apply outline-none outline-0; } + + .p-message-info & { } + + .p-message-success & { } + + .p-message-warn & { } + + .p-message-error & { } + + .p-message-secondary & { } + + .p-message-contrast & { } + } + + &-close-icon { + @apply text-body-2 w-6 h-6; } - &-close { - @apply flex items-center justify-center shrink-0; // rounded-full transition duration-200 min-w-[1rem] min-h-[1rem]; + &-info { + @apply bg-info text-white outline-none; - &.p-link { - @apply ml-auto overflow-hidden relative; - } + &.p-message-outlined { } + + &.p-message-simple { } } - & &-close { - @apply w-8 h-8 rounded-full bg-transparent transition-none outline-transparent - hover:bg-white/50 - focus-visible:outline-none focus-visible:outline-offset-0 focus-visible:drop-shadow-lg; + &-success { + @apply bg-success text-white outline-none; + + &.p-message-outlined { } + + &.p-message-simple { } + } + + &-warn { + @apply bg-warning text-gray-90 outline-none; + + &.p-message-outlined { } + + &.p-message-simple { } + } + + &-error { + @apply bg-danger text-white outline-none; + + &.p-message-outlined { } + + &.p-message-simple { } + } + + &-secondary { + @apply bg-secondary text-white outline-none; + + &.p-message-outlined { } + + &.p-message-simple { } + } + + &-contrast { + @apply bg-gray-90 text-white outline-none; + + &.p-message-outlined { } + + &.p-message-simple { } + } + + &-text { + @apply text-body-2 font-normal; } &-enter-from { @@ -40,70 +90,70 @@ } &-enter-active { - @apply transition-opacity duration-300; + @apply transition-opacity duration-200; } &.p-message-leave-from { - @apply max-h-96; + @apply max-h-[1000px]; } - &.p-message-leave-to { + &.p-message-leave-from { @apply max-h-0 opacity-0 m-0; } &-leave-active { @apply overflow-hidden transition; - } - &-leave-active &-close { - @apply hidden; + & .p-message-close-button { + @apply opacity-0; + } } - & &-text { - @apply text-body-2 w-full; - - a { - @apply font-semibold; + &-sm { + & .p-message-content { + @apply p-2; } - } - .p-message-icon { - @apply leading-normal; - } + & .p-message-text { + @apply text-body-2; + } - &.p-message-info { - @apply bg-info text-white; + & .p-message-icon { + @apply text-body-1 w-4 h-4; + } - .p-message-icon, - .p-message-close { - @apply text-white; + &.p-message-close-icon { + @apply text-body-2 w-4 h-4; } } - &.p-message-success { - @apply bg-success text-white; + &-lg { + & .p-message-content { + @apply p-6; + } - .p-message-icon, - .p-message-close { - @apply text-white; + & .p-message-text { + @apply text-body-1; } - } - &.p-message-warn { - @apply bg-warning text-gray-90; + & .p-message-icon { + @apply text-h5 w-8 h-8; + } - .p-message-icon, - .p-message-close { - @apply text-gray-90; + &.p-message-close-icon { + @apply text-body-2 w-6 h-6; } } - &.p-message-error { - @apply bg-error text-white; + &-message-outlined { + @apply bg-transparent outline-none; + } + + &-simple { + @apply bg-transparent outline-none; - .p-message-icon, - .p-message-close { - @apply text-white; + & .p-message-content { + @apply p-0; } } } diff --git a/assets/css/scss/atoms/_password.scss b/assets/css/scss/atoms/_password.scss index 01d780066a2..ff0eb981a15 100644 --- a/assets/css/scss/atoms/_password.scss +++ b/assets/css/scss/atoms/_password.scss @@ -1,21 +1,56 @@ .p-password { - @apply inline-flex w-full; -} + @apply inline-flex relative w-full; -.p-password-input { - &::-ms-reveal, - &::-ms-clear { - @apply hidden; + & &-overlay { + @apply min-w-full; } -} -.p-float-label { - .p-password { - &.p-inputwrapper-filled, - &.p-inputwrapper-focus { - ~ label { - @apply top-0 text-primary text-caption; - } + &-meter { + @apply rounded-lg; + + &-label { + @apply h-full w-0 rounded-lg; + } + + &-weak { + @apply bg-danger; + } + + &-medium { + @apply bg-warning; + } + + &-strong { + @apply bg-success; } } + + &-fluid { + @apply flex; + + & .p-password-input { + @apply w-full; + } + } + + &-input::-ms-reveal, + &-input::-ms-clear { + display: none; + } + + &-overlay { + @apply p-4 bg-white text-gray-90 border-none border-0 outline-none outline-0 drop-shadow-lg rounded-lg; + } + + &-content { + @apply flex flex-col gap-4; + } + + &-toggle-mask-icon { + @apply inset-y-0 right-4 text-gray-90 absolute w-4 h-full; + } + + &:has(&-mask-icon) &-input { + @apply pr-4; + } } diff --git a/assets/css/scss/atoms/_radio.scss b/assets/css/scss/atoms/_radio.scss index cfa9ac244e9..8917e8cf5dd 100644 --- a/assets/css/scss/atoms/_radio.scss +++ b/assets/css/scss/atoms/_radio.scss @@ -2,41 +2,32 @@ @apply relative inline-flex select-none align-bottom w-4 h-4; &-input { - @apply cursor-pointer; + @apply cursor-pointer appearance-none absolute top-0 start-0 w-full h-full p-0 m-0 opacity-0 z-[1] outline-none outline-0 border-2 border-transparent rounded-full; } &-box { - @apply flex justify-center items-center; + @apply flex justify-center items-center rounded-full border border-gray-50 bg-white w-4 h-4 transition outline-transparent drop-shadow-none; } &-icon { - @apply rounded-full invisible; - -webkit-backface-visibility: hidden; + @apply duration-200 w-1.5 h-1.5 rounded-full transform-gpu; backface-visibility: hidden; - transform: translateZ(0) scale(0.1); } - &:has(&-input--legacy:checked) &-icon, - &.p-highlight &-icon { - @apply visible; - transform: translateZ(0) scale(1, 1); - } - - & &-input { - @apply appearance-none absolute top-0 left-0 w-full h-full p-0 m-0 opacity-0 z-[1] outline-none border-2 border-support-3; - } - - & &-box { - @apply border-2 border-support-3 bg-white w-4 h-4 text-primary rounded-full transition-none outline-none; + &:not(.p-disabled) { + &:has(.p-radiobutton-input:hover) { + .p-radiobutton-box { + @apply border-primary; + } + } } - & &-box &-icon { - @apply w-1.5 h-1.5 duration-200 bg-white; + &-checked &-box { + @apply border-gray-50 bg-white; } - &:has(&-input--legacy:checked) &-box, - &.p-highlight &-box { - @apply border-primary bg-primary; + &-checked &-box &-icon { + @apply bg-gray-90 transform-gpu visible; } &:not(.p-disabled):has(&-input:hover) { @@ -44,19 +35,70 @@ @apply border-primary; } - &:has(.p-radiobutton-input--legacy:checked) .p-radiobutton-box, - &.p-highlight .p-radiobutton-box { - @apply border-primary bg-primary text-white; + &.p-radiobutton-checked .p-radiobutton-box .p-radiobutton-icon { + @apply bg-primary; } } - &:not(.p-disabled):has(&-input:focus-visible) { - .p-radiobutton-box { - @apply outline-none drop-shadow-lg bg-primary; + &-checked:not(.p-disabled) { + &:has(.p-radiobutton-input:hover) .p-radiobutton-box { + @apply border-primary bg-white; + } + + &:has(.p-radiobutton-input:focus-visible) .p-radiobutton-box { + @apply border-gray-50; } } - &.p-invalid > &-box { - @apply border-error; + &:not(.p-disabled):has(&-input:focus-visible) &-box { + @apply border-primary drop-shadow-lg outline-none outline-0; + } + + .p-radiobutton.p-invalid > .p-radiobutton-box { + @apply border-danger; + } + + &.p-variant-filled { + & .p-radiobutton-box { + @apply bg-white; + } + + &.p-radiobutton-checked .p-radiobutton-box { + @apply bg-white; + } + + &:not(.p-disabled):has(.p-radiobutton-input:hover).p-radiobutton-checked .p-radiobutton-box { + @apply bg-white; + } + } + + &.p-disabled { + @apply opacity-100; + } + + &.p-disabled &-box { + @apply bg-gray-50 border-gray-50 + } + + &-checked.p-disabled &-box &-icon { + @apply bg-gray-50; + } + + &-sm, + &-sm &-box { + @apply w-2 h-2; + } + + &-sm &-icon { + @apply text-body-2 w-1.5 h-1.5; + } + + &-lg, + &-lg &-box { + @apply w-6 h-6; + } + + &-lg &-icon { + @apply text-body-2 w-4 h-4; } -} \ No newline at end of file +} diff --git a/assets/css/scss/atoms/_select.scss b/assets/css/scss/atoms/_select.scss new file mode 100644 index 00000000000..a94eed1bb0f --- /dev/null +++ b/assets/css/scss/atoms/_select.scss @@ -0,0 +1,132 @@ +.p-select { + @apply inline-flex cursor-pointer relative select-none bg-white border border-gray-50 transition rounded-lg outline-none shadow-none drop-shadow-none w-full; + + &:not(.p-disabled) { + @apply hover:border-primary hover:text-gray-90; + + &.p-focus { + @apply border-primary text-gray-90 outline-0 outline-none + } + } + + &.p-variant-filled { + &:not(.p-disabled) { + &:hover {} + + &.p-focus {} + } + } + + &.p-invalid { + @apply border-danger text-danger; + } + + &.p-disabled { + @apply bg-gray-25 opacity-60; + } + + &-clear-icon { + @apply absolute top-1/2 -mt-2; + } + + &-dropdown { + @apply flex items-center justify-center shrink-0 bg-transparent rounded-r-lg pr-4 py-2 text-gray-90 text-body-2; + + .p-select-sm & .p-icon {} + + .p-select-lg & .p-icon {} + } + + &-label { + @apply block whitespace-nowrap overflow-hidden flex-auto w-[1%] px-4 py-3 overflow-ellipsis cursor-pointer text-gray-90 text-body-2 bg-transparent border-none border-0 outline-none outline-0; + + &.p-placeholder { + @apply text-gray-50; + + .p-select.p-invalid & {} + } + + .p-select:has(.p-select-clear-icon) & {} + + .p-select.p-disabled & { + @apply text-gray-50; + } + + &-empty { + @apply overflow-hidden opacity-0; + } + + .p-select-sm & { + } + + .p-select-lg & {} + } + + input.p-select-label { + @apply cursor-default; + } + + & &-overlay { + @apply cursor-default; + } + + &-overlay { + @apply absolute top-0 left-0 mt-2 bg-white text-gray-90 border-0 border-none rounded-lg drop-shadow-lg; + } + + &-header {} + + &-filter { + @apply w-full; + } + + &-list-container { + @apply overflow-auto; + } + + &-option-group { + + } + + &-list { + @apply m-0 p-0 list-none gap-0 flex flex-col + before:content-[""] before:w-full before:block before:h-3 before:sticky before:bg-white before:z-[2] before:top-0 before:rounded-t-lg before:rounded-b-none + after:content-[""] after:w-full after:block after:h-3 after:sticky after:bg-white after:z-[2] after:bottom-0 after:rounded-b-lg after:rounded-t-none; + } + + &-option { + @apply cursor-pointer text-body-2 whitespace-nowrap relative overflow-hidden flex items-center px-4 py-2 border-0 border-none text-gray-90 transition; + + &:not(.p-select-option-selected):not(.p-disabled).p-focus { + @apply bg-white text-primary; + } + + &.p-select-option-selected { + @apply bg-support-1 text-primary; + + &.p-focus { + @apply bg-support-1 text-primary; + } + } + } + + &-option-blank-icon { + @apply shrink-0 + } + + &-option-check-icon { + @apply relative shrink-0 text-support-3; + } + + &-empty-message { + @apply px-4 py-2; + } + + &-fluid { + @apply flex w-full; + } +} + +select.p-select { + @apply px-4 py-2; +} diff --git a/assets/css/scss/index.scss b/assets/css/scss/index.scss index f62e4dae56b..33967759188 100755 --- a/assets/css/scss/index.scss +++ b/assets/css/scss/index.scss @@ -35,8 +35,8 @@ @include meta.load-css("atoms/checkbox"); @include meta.load-css("atoms/color_picker"); @include meta.load-css("atoms/divider"); -@include meta.load-css("atoms/dropdown"); @include meta.load-css("atoms/fieldset"); +@include meta.load-css("atoms/float_label"); @include meta.load-css("atoms/inline_message"); @include meta.load-css("atoms/inputgroup"); @include meta.load-css("atoms/input_switch"); @@ -48,6 +48,7 @@ @include meta.load-css("atoms/progressbar"); @include meta.load-css("atoms/radio"); @include meta.load-css("atoms/rating"); +@include meta.load-css("atoms/select"); @include meta.load-css("atoms/skeleton"); @include meta.load-css("atoms/tags"); @include meta.load-css("atoms/toast"); @@ -55,7 +56,6 @@ @include meta.load-css("molecules/chip"); @include meta.load-css("molecules/course_tool"); -@include meta.load-css("molecules/datepicker"); @include meta.load-css("molecules/empty_state"); @include meta.load-css("molecules/teacher_bar"); @include meta.load-css("molecules/toolbar"); @@ -66,7 +66,7 @@ @include meta.load-css("organisms/datatable"); @include meta.load-css("organisms/dataview"); @include meta.load-css("organisms/external_logins"); -@include meta.load-css("organisms/modals"); +@include meta.load-css("organisms/dialog"); @include meta.load-css("organisms/menu"); @include meta.load-css("organisms/sidebar"); @include meta.load-css("organisms/section_header"); diff --git a/assets/css/scss/layout/_topbar.scss b/assets/css/scss/layout/_topbar.scss index 29072e39a65..21d2d570516 100644 --- a/assets/css/scss/layout/_topbar.scss +++ b/assets/css/scss/layout/_topbar.scss @@ -1,233 +1,199 @@ .app-topbar { - @apply flex bg-white border-b border-solid border-gray-25 fixed left-0 p-4 top-0 w-full text-gray-50 z-10 - md:text-gray-90; + @apply flex bg-white border-b border-solid border-gray-25 fixed left-0 top-0 w-full z-10 py-4; - &__start { - @apply mr-auto; - } - - &__items { - @apply flex grow justify-end items-center gap-6; - } - - &__end { - @apply ml-6 flex items-center gap-6; - } - - .item-button { - @apply w-6 h-6 relative; - - .mdi, - &__icon { - @apply text-gray-50 inline-block transition-colors - hover:text-gray-90; - font-size: 18px; - line-height: 18px; - height: 18px; - width: 18px; - - &::before { - font-size: 18px; - line-height: 18px; - } - } + .p-menubar { + @apply text-gray-90 container mx-auto; - &__badge { - @apply bg-error text-white rounded-full text-tiny font-semibold absolute text-center h-4 w-4 -top-1 -right-1; + .p-menubar-start { + @apply ml-4 mr-auto + md:ml-0; } - &--unread { - @apply text-gray-90; + .p-menubar-button { + @apply w-6 h-6 text-gray-50 mx-4 my-2 outline-none; - .item-button__icon, - .mdi { - @apply text-gray-90; + .p-icon { + @apply w-[18px] h-[18px]; } } - } - - .user-avatar { - @apply rounded-full w-6 h-6 text-white bg-primary leading-6 text-body-2 font-semibold; - - img { - @apply rounded-full w-6 h-6; - } - } - - .p-menubar-button { - @apply hidden cursor-pointer items-center justify-center; - } - - .p-menubar { - @apply flex container mx-auto; - - .p-menuitem-link { - @apply cursor-pointer flex items-center overflow-hidden relative py-1 px-4 - focus:outline-none focus:outline-offset-0 focus:text-primary hover:text-primary; - } &.p-menubar-mobile { - @apply justify-between; + @apply bg-white w-full; .p-menubar-root-list { - @apply absolute hidden w-full pt-8; + @apply pt-8; + height: calc(100vh - 3.5rem - 1px); - > .p-menuitem { - @apply w-full px-6 pb-2; + > .p-menubar-item { + @apply w-full px-6 pb-2 text-gray-50; - > .p-submenu-list { - @apply right-auto; + > .p-menubar-item-content .p-menubar-item-link { + @apply py-3 px-9 + hover:border-l-4 hover:border-primary hover:border-solid hover:pl-8 hover:bg-support-1 + focus:border-l-4 focus:border-primary focus:border-solid focus:pl-8 focus:bg-support-1; } + } - &:last-child { - @apply border-t border-solid border-gray-25 py-5 mt-auto; + > .p-menubar-item:last-child { + @apply border-t border-solid border-gray-25 py-5 px-6 mt-auto; - > .p-menuitem-content .p-menuitem-link { - @apply rounded-lg - hover:bg-primary hover:text-white - focus:bg-primary focus:text-white; - } + > .p-menubar-item-content .p-menubar-item-link { + @apply py-2 px-4 rounded-lg border-0 + hover:bg-primary hover:text-white + focus:bg-primary focus:text-white; + } - &.p-menuitem-active > .p-menuitem-content .p-menuitem-link { + &.p-menubar-item-active { + > .p-menubar-item-content .p-menubar-item-link { @apply bg-primary text-white; } - - .p-menuitem-link { - @apply py-1 px-4 - hover:border-l-0 hover:border-none hover:pl-4 - focus:border-l-0 focus:border-none focus:pl-4; - } } } - } - - .p-menubar-button { - @apply rounded-lg flex h-8 w-8 transition-none outline-transparent - hover:bg-white hover:text-gray-50 focus:outline-0 focus:outline-offset-0; - } - - .p-menuitem-link { - @apply py-2.5 px-9 - hover:border-l-4 hover:border-primary hover:border-solid hover:pl-8 hover:bg-support-1 - focus:border-l-4 focus:border-primary focus:border-solid focus:pl-8 focus:bg-support-1; - } - .p-submenu-list { - @apply max-h-[15.5rem] overflow-y-auto absolute border-0 bottom-16 -mb-0.5; - width: calc(100% - 3rem); + .p-menubar-submenu { + @apply max-h-[15.5rem] overflow-y-auto absolute border-0 bottom-16 left-6; + width: calc(100vw - 3rem); + } } } + } - &.p-menubar-mobile-active { - .p-menubar-root-list { - @apply flex flex-col left-0 z-[1]; + .p-menubar-root-list { + @apply bg-white top-14 mt-[1px] outline-none + md:justify-end grow md:flex-wrap md:items-center; - top: calc(100% + 1px); - height: calc(100vh - 4.5rem - 1px); - } + > .p-menubar-item:last-child .p-menubar-submenu { + @apply md:right-0; } + } - .p-submenu-list { - @apply hidden absolute z-[1] px-0 bg-white drop-shadow-lg rounded-lg - after:content-[""] after:w-full after:block after:h-3 after:sticky after:bg-white after:z-[2] after:bottom-0 after:rounded-b-lg - before:content-[""] before:w-full before:block before:h-3 before:sticky before:bg-white before:z-[2] before:top-0 before:rounded-b-lg; + .p-menubar-item-content { + @apply md:py-2; - > .p-menuitem-active > .p-submenu-list { - @apply block left-full top-0; - } + .p-menubar-item-link { + @apply font-semibold + hover:outline-none hover:outline-offset-0 hover:text-primary + focus:outline-none focus:outline-offset-0 focus:text-primary + md:px-4 md:py-2 md:text-body-1; } } - .p-menubar-root-list { - @apply flex items-center flex-wrap outline-0 bg-white grow justify-end relative; + .p-menubar-item { + @apply relative; + } - > li ul { - @apply hidden z-[1]; - } + .p-menubar-submenu { + @apply bg-white outline-none rounded-lg drop-shadow-lg py-3; - > .p-menuitem-active > .p-submenu-list { - @apply block right-0; + .p-menubar-item { + .p-menubar-item-content { + @apply md:p-0; + + .p-menubar-item-link { + @apply bg-white text-body-1 py-2 px-4 font-normal + hover:bg-support-1 + focus:bg-support-1 + active:bg-support-1 active:text-primary; + } + } } } -} -.app-topbar__user-submenu { - .p-menuitem-link { - @apply rounded-none py-1 px-4 transition-none select-none; + &__start { + @apply ml-4 mr-auto; + } - &:focus { - @apply outline-none outline-offset-0; - } + &__items { + @apply flex grow justify-end items-center gap-6; - &:hover, - &:focus { - @apply text-primary; - } - } -} + .item-button { + @apply w-8 h-8 relative text-center; -.app-topbar .p-menubar-root-list { - .p-menuitem-icon { - @apply mr-2; + .mdi, + &__icon { + @apply text-gray-50 inline-block transition-colors align-middle text-2xl leading-6 w-6 h-6 + hover:text-gray-90; - &:empty { - @apply mx-0; + &::before { + @apply text-2xl leading-6; + } + } + + &__badge { + @apply bg-error text-white rounded-full text-tiny font-semibold absolute text-center h-4 w-4 top-0 right-0; + } + + &.router-link-exact-active .mdi { + @apply text-gray-90; + } } } - > .p-menuitem { - > .p-menuitem-link { + &__end { + @apply ml-6 mr-4 flex items-center gap-6; - .p-submenu-icon { - @apply mr-2 -order-1; - } + .user-avatar { + @apply rounded-full w-8 h-8 text-white bg-primary leading-6 text-body-2 font-semibold; - &:focus { - @apply outline-none outline-offset-0; + img { + @apply rounded-full w-8 h-8; } } - &.p-menuitem-active { - > .p-menuitem-link { - @apply text-primary; + .item-button { + @apply w-8 h-8 relative; + + .mdi, + &__icon { + @apply text-gray-50 inline-block transition-colors align-middle text-2xl leading-6 w-6 h-6 + hover:text-gray-90; + + &::before { + @apply text-2xl leading-6; + } } } } -} -.app-topbar__user-submenu { - @apply bg-white border-none rounded-lg drop-shadow-lg py-3 px-0 w-52 max-h-60 overflow-y-auto; + &__user-submenu { + @apply mt-1 border-0 drop-shadow-lg rounded-lg; - .p-submenu-header { - @apply rounded-t-none font-semibold m-0 py-3 px-4; - } - .p-menu-separator { - @apply border-t border-solid my-1 mx-0; - } + ul.p-menu-list { + @apply outline-none; + } - &.p-menu-overlay { - @apply border-none drop-shadow-lg; - } + .p-menu-submenu-label { + @apply text-body-1 font-semibold py-1 px-4; + } + + .p-menu-item-link { + @apply rounded-none py-1 px-4 transition-none select-none flex gap-1; + + &:focus { + @apply outline-none outline-offset-0; + } - .p-menuitem { - .p-menuitem-link { &:hover, &:focus { - @apply bg-support-1; + @apply text-primary bg-support-1; + } + + .p-menu-item-label { + @apply text-body-1; } } } } -.app-topbar .p-menubar { - .p-submenu-list { +.app-topbars .p-menubar { + .p-menubar-submenu { .p-submenu-icon { } - >.p-menuitem:hover, - >.p-menuitem:focus { - > .p-menuitem-link { + >.p-menubar-item:hover, + >.p-menubar-item:focus { + > .p-menubar-item-link { @apply bg-support-1; } } @@ -235,7 +201,7 @@ } @media (max-width: 639px) { - .app-topbar .p-menubar { + .app-topbars .p-menubar { &.p-menubar-mobile-active { + .app-main { @apply max-h-screen overflow-hidden; diff --git a/assets/css/scss/molecules/_datepicker.scss b/assets/css/scss/molecules/_datepicker.scss deleted file mode 100644 index 8f31f8f23ac..00000000000 --- a/assets/css/scss/molecules/_datepicker.scss +++ /dev/null @@ -1,133 +0,0 @@ -.p-calendar { - &.p-invalid { - &.p-component { - > .p-inputtext { - @apply border-error; - } - } - } -} - -.p-datepicker { - @apply bg-white p-0 rounded-lg text-body-2; - - &:not(.p-datepicker-inline) { - @apply shadow-lg; - } - - .p-datepicker-calendar-container { - @apply flex gap-2 p-2; - } - - .p-datepicker-header { - @apply border-b border-solid border-gray-30 flex gap-2 rounded-t-lg p-2; - - .p-datepicker-prev, - .p-datepicker-next { - @apply cursor-default font-semibold p-2 text-gray-50; - - &:enabled { - @apply hover:outline-none hover:text-gray-90 focus:outline-none focus:text-gray-90; - } - } - - .p-datepicker-title { - @apply flex font-semibold gap-2; - - .p-datepicker-year, - .p-datepicker-month { - @apply cursor-default p-2 text-gray-90; - - &:enabled { - @apply hover:text-black; - } - } - } - } - - table { - - th { - @apply px-0 py-1; - - > span { - @apply text-gray-90 h-8 w-8; - } - } - - tbody { - @apply border-none; - } - - td { - @apply p-0.5; - - > span { - @apply cursor-default h-8 rounded-lg w-8 - focus:outline-none focus:outline-offset-0; - } - - > span.p-highlight { - @apply bg-support-3 text-support-4; - } - } - - td.p-datepicker-today { - > span { - @apply bg-gray-30; - - &.p-highlight { - @apply bg-support-3 text-support-4; - } - } - } - } - - .p-timepicker { - @apply border-t border-solid border-gray-30 flex font-semibold justify-center gap-2 rounded-b-lg p-2; - - button { - @apply cursor-default h-8 w-8 text-gray-50; - - &:enabled { - @apply hover:outline-none hover:text-gray-90 focus:outline-none focus:text-gray-90; - } - } - } - - .p-monthpicker { - @apply gap-2 grid grid-cols-3 grid-rows-4 p-2; - - .p-monthpicker-month { - @apply rounded-lg mx-auto p-2; - } - - .p-monthpicker-month.p-highlight { - @apply bg-support-3 text-support-4; - } - } - - .p-yearpicker { - @apply gap-2 grid grid-cols-2 p-2; - - .p-yearpicker-year { - @apply rounded-lg mx-auto p-2; - } - - .p-yearpicker-year.p-highlight { - @apply bg-support-3 text-support-4; - } - } - - &:not(.p-disabled) { - table td span, - .p-monthpicker .p-monthpicker-month, - .p-yearpicker .p-yearpicker-year { - &:not(.p-disabled) { - &:not(.p-highlight) { - @apply hover:bg-gray-50; - } - } - } - } -} diff --git a/assets/css/scss/organisms/_modals.scss b/assets/css/scss/organisms/_dialog.scss similarity index 53% rename from assets/css/scss/organisms/_modals.scss rename to assets/css/scss/organisms/_dialog.scss index 140ea278597..b9acfc910d8 100644 --- a/assets/css/scss/organisms/_modals.scss +++ b/assets/css/scss/organisms/_dialog.scss @@ -1,7 +1,7 @@ .p-dialog { @apply max-h-[90%] scale-100 drop-shadow-lg rounded-lg border-0 border-none; - .p-dialog-header { + &-header { @apply bg-white border-b border-solid border-gray-30 gap-4 rounded-t-lg p-4; .p-dialog-title { @@ -13,11 +13,11 @@ } } - .p-dialog-header-icons { + &-header-icons { @apply flex gap-4 items-center; } - .p-dialog-header-icon { + &-header-icon { @apply flex items-center justify-center overflow-hidden relative; &:enabled { @@ -27,31 +27,31 @@ } } } -} -.p-dialog-content { - @apply overflow-y-auto bg-white overscroll-contain p-4; -} + &-content { + @apply overflow-y-auto bg-white overscroll-contain p-4; + } -.p-dialog-header { - @apply flex items-center justify-between flex-shrink-0; -} + &-header { + @apply flex items-center justify-between flex-shrink-0; + } -.p-dialog-footer { - @apply flex-shrink-0 bg-gray-15 flex gap-4 justify-end rounded-b-lg p-4; -} + &-footer { + @apply flex-shrink-0 bg-gray-15 flex gap-4 justify-end rounded-b-lg p-4; + } -.p-dialog-enter-active { - @apply transition-all duration-150 ease-in; -} + &-enter-active { + @apply transition-all duration-150 ease-in; + } -.p-dialog-leave-active { - @apply transition-all duration-150 ease-out; -} + &-leave-active { + @apply transition-all duration-150 ease-out; + } -.p-dialog-enter-from, -.p-dialog-leave-to { - @apply opacity-0 scale-75; + &-enter-from, + &-leave-to { + @apply opacity-0 scale-75; + } } .p-dialog.p-confirm-dialog { diff --git a/assets/css/scss/organisms/_sidebar.scss b/assets/css/scss/organisms/_sidebar.scss index e5123b9904c..2b84bd83b8c 100644 --- a/assets/css/scss/organisms/_sidebar.scss +++ b/assets/css/scss/organisms/_sidebar.scss @@ -13,7 +13,7 @@ } &__panel { - @apply pt-8 px-0 overflow-x-hidden overflow-y-auto flex-1 overscroll-none + @apply pt-8 px-0 overflow-x-hidden overflow-y-auto flex-1 overscroll-none flex flex-col gap-1 sm:pt-5; } @@ -50,103 +50,136 @@ @apply border-primary; } - &.p-button.p-button-icon-only { - @apply h-8 p-0 w-8; - } - - .p-button-label { + .p-togglebutton-label { @apply hidden; } - - .p-togglebutton-input { - @apply appearance-none absolute top-0 left-0 w-full h-full p-0 m-0 opacity-0 outline-0 outline-none; - z-index: 1; - } - - .p-button { - @apply bg-white p-0 text-primary; - } } .p-panelmenu { + @apply flex flex-col gap-1; .p-panelmenu-header-action { - @apply flex items-center gap-2 select-none cursor-pointer relative pl-9 py-3 pr-6 mb-1 overflow-hidden; + @apply flex items-center gap-2 select-none cursor-pointer relative pl-9 py-3 pr-6 overflow-hidden outline-none text-body-2 font-semibold; } .p-menuitem-link { @apply flex items-center select-none cursor-pointer relative overflow-hidden; } + } - .p-panelmenu-header { - @apply outline-none; + .p-panelmenu-panel { - &.p-highlight, - &:hover { - @apply bg-support-1 border-l-4 border-primary border-solid text-primary; + &:first-child { + } - .p-panelmenu-header-action { - @apply pl-8; - } - } + &:last-child { + } + } - .p-panelmenu-header-content { + .p-panelmenu-header { + @apply outline-none; - .p-panelmenu-header-action { + &.p-panelmenu-header-active, + &:hover { + @apply bg-support-1 border-l-4 border-primary border-solid text-primary; - .p-submenu-icon { - @apply order-3 text-body-2 font-semibold; - } + .p-panelmenu-header-action { + @apply pl-8; + } + } - .p-menuitem-icon { - @apply order-1 text-body-2 font-semibold; - } + .p-panelmenu-header-content { + + .p-panelmenu-header-action { + + .p-submenu-icon { + @apply ml-auto text-body-2 font-semibold; } } + } - > .p-panelmenu-header-content a { - .p-menuitem-text { - @apply order-2 grow text-body-2 font-semibold; - } + > .p-panelmenu-header-content a { + .p-menuitem-text { + @apply order-2 grow text-body-2 font-semibold; } } + } - .p-panelmenu-content { + .p-panelmenu-header-content { + } - .p-menuitem { + .p-panelmenu-header-link { + } - > .p-menuitem-content { + .p-panelmenu-header-icon, + .p-panelmenu-item-icon { + // @apply text-gray-50; + } - .p-menuitem-link { - @apply pl-8 py-3 pr-6; + .p-panelmenu-submenu-icon { + // @apply text-gray-50; + @apply rtl:rotate-180; + } - .p-menuitem-icon { - @apply mr-2 leading-none; - } + .p-panelmenu-header:not(.p-disabled):focus-visible .p-panelmenu-header-content { + } - .p-menuitem-text { - @apply text-body-2 - hover:text-primary; - } + .p-panelmenu-header:not(.p-disabled):focus-visible .p-panelmenu-header-content .p-panelmenu-header-icon { + } + + .p-panelmenu-header:not(.p-disabled):focus-visible .p-panelmenu-header-content .p-panelmenu-submenu-icon { + } + + .p-panelmenu-submenu { + @apply m-0 p-0 outline-none; + + .p-menuitem-link { + @apply flex items-center gap-2 select-none cursor-pointer relative pl-9 py-3 pr-6 overflow-hidden outline-none text-body-2; + } + } + + .p-panelmenu-header:not(.p-disabled) .p-panelmenu-header-content:hover { + } + + .p-panelmenu-content { + + .p-menuitem { + + > .p-menuitem-content { + + .p-menuitem-link { + @apply pl-8 py-3 pr-6; + + .p-menuitem-icon { + @apply mr-2 leading-none; + } + + .p-menuitem-text { + @apply text-body-2 + hover:text-primary; } } + } - &.p-highlight, - &.p-focus { + &.router-link-exact-active, + &.p-focus { - > .p-menuitem-content { + > .p-menuitem-content { - .p-menuitem-link { + .p-menuitem-link { - .p-menuitem-text { - @apply text-primary; - } + .p-menuitem-text { + @apply text-primary; } } } } } } + + .router-link-active { + @apply text-primary; + } } .app-sidebar__topbar-button { @@ -169,7 +202,7 @@ @apply sm:hidden; } - .p-menuitem-text { + .p-panelmenu-header-label { @apply sm:hidden; } } diff --git a/assets/vue/components/Login.vue b/assets/vue/components/Login.vue index db6e90d78ef..58dc951b25f 100644 --- a/assets/vue/components/Login.vue +++ b/assets/vue/components/Login.vue @@ -6,7 +6,7 @@ />
@@ -15,6 +15,7 @@ v-model="login" :placeholder="t('Username')" type="text" + variant="filled" />
@@ -25,6 +26,7 @@ :placeholder="t('Password')" input-id="password" toggle-mask + variant="filled" /> @@ -36,11 +38,12 @@ v-model="totp" :placeholder="t('Enter 2FA code')" type="text" + variant="filled" />
- import { provide, ref } from "vue" import { useI18n } from "vue-i18n" -import BaseInputDate from "../basecomponents/BaseInputDate.vue" +import BaseCalendar from "../basecomponents/BaseCalendar.vue" import BaseButton from "../basecomponents/BaseButton.vue" import BaseMenu from "../basecomponents/BaseMenu.vue" import BaseDialogConfirmCancel from "../basecomponents/BaseDialogConfirmCancel.vue" -import BaseDropdown from "../basecomponents/BaseDropdown.vue" +import BaseSelect from "../basecomponents/BaseSelect.vue" import BaseRadioButtons from "../basecomponents/BaseRadioButtons.vue" import BaseCheckbox from "../basecomponents/BaseCheckbox.vue" import BaseInputText from "../basecomponents/BaseInputText.vue" @@ -136,24 +136,7 @@ provide("isCustomizing", isCustomizing) ref="menu" :model="menuItems" /> - -
diff --git a/assets/vue/components/assignments/AssignmentsForm.vue b/assets/vue/components/assignments/AssignmentsForm.vue index dc01e8e5533..104d302c6ed 100644 --- a/assets/vue/components/assignments/AssignmentsForm.vue +++ b/assets/vue/components/assignments/AssignmentsForm.vue @@ -30,7 +30,7 @@ />
- - - - diff --git a/assets/vue/components/basecomponents/BaseDropdown.vue b/assets/vue/components/basecomponents/BaseDropdown.vue deleted file mode 100644 index ac83087840d..00000000000 --- a/assets/vue/components/basecomponents/BaseDropdown.vue +++ /dev/null @@ -1,90 +0,0 @@ - - - diff --git a/assets/vue/components/basecomponents/BaseInputDate.vue b/assets/vue/components/basecomponents/BaseInputDate.vue deleted file mode 100644 index 354a7d65f39..00000000000 --- a/assets/vue/components/basecomponents/BaseInputDate.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - diff --git a/assets/vue/components/basecomponents/BaseSelect.vue b/assets/vue/components/basecomponents/BaseSelect.vue index 7484198fef9..50f8f3354f4 100644 --- a/assets/vue/components/basecomponents/BaseSelect.vue +++ b/assets/vue/components/basecomponents/BaseSelect.vue @@ -1,28 +1,38 @@ @@ -30,35 +40,44 @@ import { useI18n } from "vue-i18n" import { computed } from "vue" import FloatLabel from "primevue/floatlabel" +import Dropdown from "primevue/select" +import Message from "primevue/message" const { t } = useI18n() +const modelValue = defineModel({ + type: [String, Number, Object], +}) + const props = defineProps({ id: { type: String, require: true, default: "", }, + name: { + type: String, + required: false, + default: undefined, + }, label: { type: String, required: true, default: "", }, - modelValue: { - type: [String, Number, null], - required: true, - }, options: { type: Array, required: true, }, optionLabel: { type: String, - required: true, + required: false, + default: "label", }, optionValue: { type: String, - required: true, + required: false, + default: "value", }, isInvalid: { type: Boolean, @@ -82,9 +101,19 @@ const props = defineProps({ required: false, type: Boolean, }, + placeholder: { + type: String, + required: false, + default: "", + }, + messageText: { + type: [String, null], + required: false, + default: null, + }, }) -const emit = defineEmits(["update:modelValue", "change"]) +const emit = defineEmits(["change"]) const realOptions = computed(() => { if (props.hastEmptyValue) { diff --git a/assets/vue/components/ccalendarevent/CCalendarEventForm.vue b/assets/vue/components/ccalendarevent/CCalendarEventForm.vue index 322fe84fdc8..d9953096b8a 100644 --- a/assets/vue/components/ccalendarevent/CCalendarEventForm.vue +++ b/assets/vue/components/ccalendarevent/CCalendarEventForm.vue @@ -13,8 +13,6 @@ :initial-value="[item.startDate, item.endDate]" :is-invalid="v$.item.startDate.$invalid || v$.item.endDate.$invalid" :label="t('Date')" - class="max-w-sm w-full" - show-icon show-time type="range" /> diff --git a/assets/vue/components/ccalendarevent/CalendarInvitations.vue b/assets/vue/components/ccalendarevent/CalendarInvitations.vue index 5c6f74b4219..45897f8afb2 100644 --- a/assets/vue/components/ccalendarevent/CalendarInvitations.vue +++ b/assets/vue/components/ccalendarevent/CalendarInvitations.vue @@ -117,8 +117,6 @@ const maxSubscriptionsDisabled = computed(() => 0 === subscriptionVisibilitySele v-model="subscriptionVisibilitySelected" :label="t('Allow subscriptions')" :options="subscriptionVisibilityList" - option-label="label" - option-value="value" /> - - diff --git a/assets/vue/components/installer/Step1.vue b/assets/vue/components/installer/Step1.vue index 8a52a7a78be..17bc3cbc4ab 100644 --- a/assets/vue/components/installer/Step1.vue +++ b/assets/vue/components/installer/Step1.vue @@ -7,9 +7,9 @@ /> - - - - -
- { const contentId = header.getAttribute("aria-controls") const contentPanel = document.getElementById(contentId) - if (contentPanel && contentPanel.querySelector(".p-toggleable-content")) { - if (!sidebarIsOpen.value) { - expandingDueToPanelClick.value = true - sidebarIsOpen.value = true - window.localStorage.setItem("sidebarIsOpen", "true") - } + if (contentPanel && !sidebarIsOpen.value) { + expandingDueToPanelClick.value = true + sidebarIsOpen.value = true + window.localStorage.setItem("sidebarIsOpen", "true") } } diff --git a/assets/vue/components/links/LinkForm.vue b/assets/vue/components/links/LinkForm.vue index c4985542d70..f5a616dda41 100644 --- a/assets/vue/components/links/LinkForm.vue +++ b/assets/vue/components/links/LinkForm.vue @@ -39,8 +39,6 @@ { label: 'Open parent', value: '_parent' }, { label: 'Open top', value: '_top' }, ]" - option-label="label" - option-value="value" />
diff --git a/assets/vue/components/page/Form.vue b/assets/vue/components/page/Form.vue index fe0ba03eab8..2cea4ac96b1 100644 --- a/assets/vue/components/page/Form.vue +++ b/assets/vue/components/page/Form.vue @@ -37,7 +37,7 @@ name="enabled" /> - -
- { if (!selectedLanguage.value) return termsLoaded.value = false try { - const response = await legalService.findAllByLanguage(selectedLanguage.value.id) + const response = await legalService.findAllByLanguage(selectedLanguage.value) if (response.ok) { const data = await response.json() const latestTerm = data["hydra:member"].length ? data["hydra:member"][0] : null @@ -179,7 +180,7 @@ const loadTermsByLanguage = async () => { } const saveTerms = async () => { const payload = { - lang: selectedLanguage.value.id, + lang: selectedLanguage.value, content: termData.value.content, type: termData.value.type.toString(), changes: termData.value.changes, @@ -216,7 +217,7 @@ const extraFields = ref([]) function getFieldComponent(type) { const componentMap = { text: BaseInputText, - select: BaseDropdown, + select: BaseSelect, editor: BaseTinyEditor, // Add more mappings as needed } diff --git a/package.json b/package.json index d274d4a70d4..a36c55acc2e 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@vuelidate/core": "^2.0.3", "@vuelidate/validators": "^2.0.4", "alpinejs": "^3.14.9", - "axios": "^1.8.4", + "axios": "^1.9.0", "blueimp-file-upload": "^10.32.0", "blueimp-load-image": "^5.16.0", "bootstrap-daterangepicker": "^3.1.0", @@ -48,12 +48,12 @@ "dropzone": "^5.9.3", "easy-pie-chart": "^2.1.7", "easytimer.js": "^1.3.2", - "eslint-plugin-prettier": "^5.2.6", + "eslint-plugin-prettier": "^5.4.0", "flag-icons": "^6.15.0", "free-jqgrid": "https://github.com/chamilo/jqGrid.git#commit=725be74a7ea9d3acc896b68b11b0fbdb36105df3", "full-icu": "^1.5.0", "glob-all": "^3.3.1", - "graphql": "^16.10.0", + "graphql": "^16.11.0", "graphql-tag": "^2.12.6", "highlight.js": "^11.11.1", "hljs": "^6.2.3", @@ -80,9 +80,9 @@ "path": "^0.12.7", "pinia": "^3.0.2", "pretty-bytes": "^5.6.0", - "primeflex": "^3.3.1", - "primeicons": "^6.0.1", - "primevue": "^3.53.1", + "primeflex": "^4.0.0", + "primeicons": "^7.0.0", + "primevue": "^4.3.3", "pwstrength-bootstrap": "3.1.3", "qtip2": "3.0.3", "readmore-js": "^2.2.1", @@ -90,7 +90,7 @@ "select2": "^4.1.0-rc.0", "signature_pad": "^3.0.0-beta.4", "sortablejs": "^1.15.6", - "sweetalert2": "^11.19.1", + "sweetalert2": "^11.21.0", "textcomplete": "^0.18.2", "timeago": "^1.6.7", "timepicker": "^1.14.1", @@ -100,15 +100,15 @@ "vue-i18n": "10.0.7", "vue-multiselect": "^3.0.0-beta.2", "vue-perfect-scrollbar": "^0.2.1", - "vue-router": "4.5.0", + "vue-router": "4.5.1", "vue-toastification": "^2.0.0-rc.5", "vuex": "^4.1.0", "vuex-map-fields": "^1.4.1" }, "devDependencies": { - "@babel/core": "^7.26.10", - "@babel/preset-env": "^7.26.9", - "@eslint/js": "^9.25.1", + "@babel/core": "^7.27.1", + "@babel/preset-env": "^7.27.1", + "@eslint/js": "^9.26.0", "@mdi/font": "^7.4.47", "@symfony/webpack-encore": "^5.1.0", "@tailwindcss/forms": "^0.5.10", @@ -117,11 +117,11 @@ "@types/d3": "^7.4.3", "@vue/compiler-sfc": "^3.5.13", "autoprefixer": "^10.4.21", - "core-js": "3.41.0", + "core-js": "3.42.0", "deepmerge": "^4.3.1", - "eslint": "^9.25.1", + "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.2", - "eslint-plugin-vue": "^10.0.0", + "eslint-plugin-vue": "^10.1.0", "file-loader": "^6.2.0", "globals": "^15.15.0", "postcss": "^8.5.3", @@ -138,7 +138,7 @@ "vue": "^3.5.13", "vue-eslint-parser": "^10.1.3", "vue-loader": "^17.4.2", - "webpack": "^5.99.6", + "webpack": "^5.99.7", "webpack-cli": "^6.0.1", "webpack-dev-server": "^5.2.1", "webpack-notifier": "^1.15.0" diff --git a/public/main/inc/lib/display.lib.php b/public/main/inc/lib/display.lib.php index b215297e3c0..e4eff5e9631 100644 --- a/public/main/inc/lib/display.lib.php +++ b/public/main/inc/lib/display.lib.php @@ -897,7 +897,7 @@ public static function select( $extra = ''; $default_id = 'id="'.$name.'" '; $extra_attributes = array_merge( - ['class' => 'p-dropdown p-component p-inputwrapper p-inputwrapper-filled'], + ['class' => 'p-select p-component p-inputwrapper p-inputwrapper-filled'], $extra_attributes ); foreach ($extra_attributes as $key => $parameter) { diff --git a/public/main/inc/lib/pear/HTML/QuickForm/select.php b/public/main/inc/lib/pear/HTML/QuickForm/select.php index a72ca137f25..b8399d76906 100644 --- a/public/main/inc/lib/pear/HTML/QuickForm/select.php +++ b/public/main/inc/lib/pear/HTML/QuickForm/select.php @@ -397,7 +397,7 @@ public function toHtml() } if (FormValidator::LAYOUT_HORIZONTAL === $this->getLayout()) { - $this->_attributes['class'] .= 'p-dropdown p-component p-inputwrapper p-inputwrapper-filled'; + $this->_attributes['class'] .= 'p-select p-component p-inputwrapper p-inputwrapper-filled'; } if (!$this->getMultiple()) { diff --git a/src/CoreBundle/Command/ImportAccessUrlCommand.php b/src/CoreBundle/Command/ImportAccessUrlCommand.php index 11b01ed8baf..fbb43ed0333 100644 --- a/src/CoreBundle/Command/ImportAccessUrlCommand.php +++ b/src/CoreBundle/Command/ImportAccessUrlCommand.php @@ -7,9 +7,11 @@ namespace Chamilo\CoreBundle\Command; use Chamilo\CoreBundle\Entity\AccessUrl; -use Chamilo\CoreBundle\Entity\User; use Chamilo\CoreBundle\Entity\ResourceType; +use Chamilo\CoreBundle\Entity\User; +use DateTime; use Doctrine\ORM\EntityManagerInterface; +use Exception; use PhpOffice\PhpSpreadsheet\IOFactory; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -37,7 +39,8 @@ protected function configure(): void $this ->addArgument('xlsx-file', InputArgument::REQUIRED, 'Path to the XLSX file') ->addArgument('base-url', InputArgument::REQUIRED, 'Base URL for subdomains (e.g., https://somedomain.com/)') - ->setHelp('This command imports AccessUrl entities from an XLSX file. The file must have a title row with "subdomain" and "description" columns. Subdomains are lowercased. The ResourceNode parent is set to AccessUrl ID = 1. If needed, removing the created URLs manually will require cleaning the access_url_rel_user and resource_node tables.'); + ->setHelp('This command imports AccessUrl entities from an XLSX file. The file must have a title row with "subdomain" and "description" columns. Subdomains are lowercased. The ResourceNode parent is set to AccessUrl ID = 1. If needed, removing the created URLs manually will require cleaning the access_url_rel_user and resource_node tables.') + ; } protected function execute(InputInterface $input, OutputInterface $output): int @@ -52,6 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $parsedUrl = parse_url($baseUrl); if (!isset($parsedUrl['host']) || !isset($parsedUrl['scheme'])) { $io->error("Invalid base URL: {$baseUrl}. Must include scheme and host (e.g., https://somedomain.com/)."); + return Command::FAILURE; } $mainDomain = $parsedUrl['host']; // e.g., 'somedomain.com' @@ -60,6 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Validate XLSX file if (!file_exists($xlsxFile) || !is_readable($xlsxFile)) { $io->error("XLSX file not found or not readable: {$xlsxFile}"); + return Command::FAILURE; } @@ -68,13 +73,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int $user = $this->entityManager->getRepository(User::class)->find($defaultUserId); if (!$user) { $io->error("User with ID {$defaultUserId} not found. Cannot assign to URLs."); + return Command::FAILURE; } // Get main AccessUrl (resource_node.parent = null) and its ResourceNode $mainAccessUrl = $this->entityManager->getRepository(AccessUrl::class)->findOneBy(['parent' => null]); if (!$mainAccessUrl) { - $io->error("Main AccessUrl not found."); + $io->error('Main AccessUrl not found.'); + return Command::FAILURE; } @@ -83,6 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $resourceType = $resourceTypeRepo->findOneBy(['title' => 'urls']); if (!$resourceType) { $io->error("ResourceType 'urls' not found."); + return Command::FAILURE; } @@ -97,8 +105,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $subdomainIndex = array_search('subdomain', $headerRow); $descriptionIndex = array_search('description', $headerRow); - if ($subdomainIndex === false || $descriptionIndex === false) { + if (false === $subdomainIndex || false === $descriptionIndex) { $io->error("Columns 'subdomain' or 'description' not found in the Excel file."); + return Command::FAILURE; } @@ -111,16 +120,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Skip empty subdomain if (empty($subdomain)) { - $io->warning("Row " . ($rowNumber + 2) . ": Skipping due to empty subdomain."); + $io->warning('Row '.($rowNumber + 2).': Skipping due to empty subdomain.'); $skippedCount++; + continue; } // Lowercase and validate subdomain $subdomain = strtolower($subdomain); if (!preg_match('/^[a-z0-9][a-z0-9\-]*[a-z0-9]$/', $subdomain)) { - $io->warning("Row " . ($rowNumber + 2) . ": Invalid subdomain '$subdomain'. Skipping."); + $io->warning('Row '.($rowNumber + 2).": Invalid subdomain '$subdomain'. Skipping."); $skippedCount++; + continue; } @@ -130,8 +141,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Check if the URL already exists $existingUrl = $this->entityManager->getRepository(AccessUrl::class)->findOneBy(['url' => $subdomainUrl]); if ($existingUrl) { - $io->warning("Row " . ($rowNumber + 2) . ": URL {$subdomainUrl} already exists. Skipping."); + $io->warning('Row '.($rowNumber + 2).": URL {$subdomainUrl} already exists. Skipping."); $skippedCount++; + continue; } @@ -141,8 +153,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $accessUrl->setDescription($description); $accessUrl->setActive(1); // Set as active $accessUrl->setCreatedBy($defaultUserId); // Set created_by - $accessUrl->setTms(new \DateTime()); // Set timestamp - //$accessUrl->setResourceNode($resourceNode); // Assign ResourceNode + $accessUrl->setTms(new DateTime()); // Set timestamp + // $accessUrl->setResourceNode($resourceNode); // Assign ResourceNode $accessUrl->addUser($user); // Associate user $accessUrl->setCreator($user); // Temporary hack as AccessUrl should be able to set this automatically $accessUrl->setParent($mainAccessUrl); // Set this URL as a child of the admin URL @@ -150,7 +162,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Persist entity $this->entityManager->persist($accessUrl); - $io->success("Row " . ($rowNumber + 2) . ": Created URL: {$subdomainUrl} with description: {$description}, assigned to user ID: {$defaultUserId}, parent AccessUrl ID: 1"); + $io->success('Row '.($rowNumber + 2).": Created URL: {$subdomainUrl} with description: {$description}, assigned to user ID: {$defaultUserId}, parent AccessUrl ID: 1"); $createdCount++; } @@ -159,8 +171,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->success("Import completed: {$createdCount} URLs created, {$skippedCount} rows skipped."); return Command::SUCCESS; - } catch (\Exception $e) { - $io->error("Error: " . $e->getMessage()); + } catch (Exception $e) { + $io->error('Error: '.$e->getMessage()); + return Command::FAILURE; } } diff --git a/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php b/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php index 8ad5fe7fb8e..b24e41e1d0b 100644 --- a/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php +++ b/src/CoreBundle/Controller/Api/CreateStudentPublicationCommentAction.php @@ -6,18 +6,18 @@ namespace Chamilo\CoreBundle\Controller\Api; -use Chamilo\CoreBundle\Controller\Api\BaseResourceFileAction; use Chamilo\CoreBundle\Entity\User; use Chamilo\CoreBundle\ServiceHelper\MessageHelper; -use Chamilo\CourseBundle\Entity\CStudentPublicationComment; use Chamilo\CourseBundle\Entity\CStudentPublication; +use Chamilo\CourseBundle\Entity\CStudentPublicationComment; use Chamilo\CourseBundle\Repository\CStudentPublicationCommentRepository; use Chamilo\CourseBundle\Repository\CStudentPublicationRepository; +use DateTime; use Doctrine\ORM\EntityManager; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\Translation\TranslatorInterface; class CreateStudentPublicationCommentAction extends BaseResourceFileAction @@ -37,7 +37,7 @@ public function __invoke( $commentEntity = new CStudentPublicationComment(); $hasFile = $request->files->get('uploadFile'); - $hasComment = trim((string) $request->get('comment')) !== ''; + $hasComment = '' !== trim((string) $request->get('comment')); if ($hasFile || $hasComment) { $result = $this->handleCreateCommentRequest( @@ -73,7 +73,7 @@ public function __invoke( $user = $security->getUser(); $qualification = $request->get('qualification', null); - $hasQualification = $qualification !== null; + $hasQualification = null !== $qualification; if ($hasFile || $hasComment) { $commentEntity->setUser($user); @@ -90,7 +90,7 @@ public function __invoke( if ($hasQualification) { $submission->setQualification((float) $qualification); $submission->setQualificatorId($user->getId()); - $submission->setDateOfQualification(new \DateTime()); + $submission->setDateOfQualification(new DateTime()); $em->persist($submission); } @@ -102,8 +102,8 @@ public function __invoke( $receiverUser = $submission->getUser(); $senderUserId = $user?->getId() ?? 0; - $subject = sprintf('New feedback for your submission "%s"', $submission->getTitle()); - $content = sprintf( + $subject = \sprintf('New feedback for your submission "%s"', $submission->getTitle()); + $content = \sprintf( 'Hello %s, there is a new comment on your assignment submission "%s". Please review it in the platform.', $receiverUser->getFullname(), $submission->getTitle() diff --git a/src/CoreBundle/Controller/Api/CreateStudentPublicationCorrectionFileAction.php b/src/CoreBundle/Controller/Api/CreateStudentPublicationCorrectionFileAction.php index ffdd090e610..e54940e258d 100644 --- a/src/CoreBundle/Controller/Api/CreateStudentPublicationCorrectionFileAction.php +++ b/src/CoreBundle/Controller/Api/CreateStudentPublicationCorrectionFileAction.php @@ -10,11 +10,12 @@ use Chamilo\CourseBundle\Entity\CStudentPublicationCorrection; use Chamilo\CourseBundle\Repository\CStudentPublicationCorrectionRepository; use Chamilo\CourseBundle\Repository\CStudentPublicationRepository; +use DateTime; use Doctrine\ORM\EntityManager; use Symfony\Component\HttpFoundation\Request; -use Symfony\Contracts\Translation\TranslatorInterface; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class CreateStudentPublicationCorrectionFileAction extends BaseResourceFileAction { @@ -55,7 +56,7 @@ public function __invoke( $submission->setDescription('Correction uploaded'); $submission->setQualification(0); - $submission->setDateOfQualification(new \DateTime()); + $submission->setDateOfQualification(new DateTime()); $submission->setAccepted(true); $submission->setExtensions($correction->getTitle()); diff --git a/src/CoreBundle/Controller/Api/CreateStudentPublicationFileAction.php b/src/CoreBundle/Controller/Api/CreateStudentPublicationFileAction.php index 1bde954a449..2f826d52123 100644 --- a/src/CoreBundle/Controller/Api/CreateStudentPublicationFileAction.php +++ b/src/CoreBundle/Controller/Api/CreateStudentPublicationFileAction.php @@ -9,11 +9,12 @@ use Chamilo\CoreBundle\Entity\User; use Chamilo\CourseBundle\Entity\CStudentPublication; use Chamilo\CourseBundle\Repository\CStudentPublicationRepository; +use DateTime; use Doctrine\ORM\EntityManager; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\Translation\TranslatorInterface; -use Symfony\Bundle\SecurityBundle\Security; class CreateStudentPublicationFileAction extends BaseResourceFileAction { @@ -44,7 +45,7 @@ public function __invoke( $studentPublication->setContainsFile(1); $studentPublication->setAccepted(true); $studentPublication->setActive(1); - $studentPublication->setSentDate(new \DateTime()); + $studentPublication->setSentDate(new DateTime()); /** @var User $user */ $user = $security->getUser(); diff --git a/src/CoreBundle/Controller/AttendanceController.php b/src/CoreBundle/Controller/AttendanceController.php index 2e231887593..07bceff218e 100644 --- a/src/CoreBundle/Controller/AttendanceController.php +++ b/src/CoreBundle/Controller/AttendanceController.php @@ -27,6 +27,7 @@ use Mpdf\Output\Destination; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xls; +use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -77,12 +78,11 @@ public function getUsersWithFaults( } $calendars = $attendance->getCalendars(); - $totalCalendars = count($calendars); + $totalCalendars = \count($calendars); $users = $userRepository->findUsersByContext($courseId, $sessionId, $groupId); - $formattedUsers = array_map(function ($user) use ($userRepository, $courseId, $groupId, $calendarRepository, $sheetRepository, $calendars) { - + $formattedUsers = array_map(function ($user) use ($userRepository, $sheetRepository, $calendars) { $absences = 0; foreach ($calendars as $calendar) { @@ -91,16 +91,16 @@ public function getUsersWithFaults( 'attendanceCalendar' => $calendar, ]); - if (!$sheet || $sheet->getPresence() === null) { + if (!$sheet || null === $sheet->getPresence()) { continue; } - if ($sheet->getPresence() !== 1) { + if (1 !== $sheet->getPresence()) { $absences++; } } - $percentage = count($calendars) > 0 ? round(($absences * 100) / count($calendars)) : 0; + $percentage = \count($calendars) > 0 ? round(($absences * 100) / \count($calendars)) : 0; return [ 'id' => $user->getId(), @@ -109,7 +109,7 @@ public function getUsersWithFaults( 'email' => $user->getEmail(), 'username' => $user->getUsername(), 'photo' => $userRepository->getUserPicture($user->getId()), - 'notAttended' => "$absences/" . count($calendars) . " ({$percentage}%)", + 'notAttended' => "$absences/".\count($calendars)." ({$percentage}%)", ]; }, $users); @@ -159,7 +159,7 @@ public function exportToPdf(int $id, Request $request): Response } $calendars = $attendance->getCalendars(); - $totalCalendars = count($calendars); + $totalCalendars = \count($calendars); $students = $this->em->getRepository(User::class)->findUsersByContext($courseId, $sessionId, $groupId); $sheetRepo = $this->em->getRepository(CAttendanceSheet::class); @@ -170,8 +170,9 @@ public function exportToPdf(int $id, Request $request): Response if ($sessionId) { $session = $this->em->getRepository(Session::class)->find($sessionId); $rel = $session?->getCourseCoachesSubscriptions() - ->filter(fn($rel) => $rel->getCourse()?->getId() === $courseId) - ->first(); + ->filter(fn ($rel) => $rel->getCourse()?->getId() === $courseId) + ->first() + ; $teacher = $rel instanceof SessionRelCourseRelUser ? $rel->getUser()?->getFullname() @@ -182,7 +183,6 @@ public function exportToPdf(int $id, Request $request): Response $teacher = $rel instanceof CourseRelUser ? $rel->getUser()?->getFullname() : null; - ; } // Header @@ -212,15 +212,16 @@ public function exportToPdf(int $id, Request $request): Response 'attendanceCalendar' => $calendar, ]); - if (!$sheetEntity || $sheetEntity->getPresence() === null) { + if (!$sheetEntity || null === $sheetEntity->getPresence()) { $row[] = ''; + continue; } $presence = $sheetEntity->getPresence(); $row[] = $stateLabels[$presence] ?? 'NP'; - if ($presence === CAttendanceSheet::ABSENT) { + if (CAttendanceSheet::ABSENT === $presence) { $absences++; } } @@ -242,26 +243,26 @@ public function exportToPdf(int $id, Request $request): Response .np { color: red; font-weight: bold; } -

' . htmlspecialchars($attendance->getTitle()) . '

+

'.htmlspecialchars($attendance->getTitle()).'

- - - + + +
Trainer:' . htmlspecialchars($teacher ?? '-') . '
Course:' . htmlspecialchars($course?->getTitleAndCode() ?? '-') . '
Date:' . date('F d, Y \a\t h:i A') . '
Trainer:'.htmlspecialchars($teacher ?? '-').'
Course:'.htmlspecialchars($course?->getTitleAndCode() ?? '-').'
Date:'.date('F d, Y \a\t h:i A').'
'; foreach ($dataTable[0] as $cell) { - $html .= ''; + $html .= ''; } $html .= ''; - foreach (array_slice($dataTable, 1) as $row) { + foreach (\array_slice($dataTable, 1) as $row) { $html .= ''; foreach ($row as $cell) { - $class = $cell === 'NP' ? ' class="np"' : ''; - $html .= "" . htmlspecialchars((string) $cell) . ""; + $class = 'NP' === $cell ? ' class="np"' : ''; + $html .= "".htmlspecialchars((string) $cell).''; } $html .= ''; } @@ -269,22 +270,22 @@ public function exportToPdf(int $id, Request $request): Response $html .= '
' . htmlspecialchars((string) $cell) . ''.htmlspecialchars((string) $cell).'
'; try { - $mpdf = new \Mpdf\Mpdf([ + $mpdf = new Mpdf([ 'orientation' => 'L', - 'tempDir' => api_get_path(SYS_ARCHIVE_PATH) . 'mpdf/', + 'tempDir' => api_get_path(SYS_ARCHIVE_PATH).'mpdf/', ]); $mpdf->WriteHTML($html); return new Response( - $mpdf->Output('', \Mpdf\Output\Destination::INLINE), + $mpdf->Output('', Destination::INLINE), 200, [ 'Content-Type' => 'application/pdf', - 'Content-Disposition' => 'attachment; filename="attendance-' . $id . '.pdf"', + 'Content-Disposition' => 'attachment; filename="attendance-'.$id.'.pdf"', ] ); - } catch (\Mpdf\MpdfException $e) { - throw new \RuntimeException('Failed to generate PDF: ' . $e->getMessage(), 500, $e); + } catch (MpdfException $e) { + throw new RuntimeException('Failed to generate PDF: '.$e->getMessage(), 500, $e); } } @@ -301,7 +302,7 @@ public function exportToXls(int $id, Request $request): Response } $calendars = $attendance->getCalendars(); - $totalCalendars = count($calendars); + $totalCalendars = \count($calendars); $students = $this->em->getRepository(User::class)->findUsersByContext($courseId, $sessionId, $groupId); $sheetRepo = $this->em->getRepository(CAttendanceSheet::class); @@ -331,15 +332,16 @@ public function exportToXls(int $id, Request $request): Response 'attendanceCalendar' => $calendar, ]); - if (!$sheetEntity || $sheetEntity->getPresence() === null) { + if (!$sheetEntity || null === $sheetEntity->getPresence()) { $row[] = ''; + continue; } $presence = $sheetEntity->getPresence(); $row[] = $stateLabels[$presence] ?? 'NP'; - if ($presence === CAttendanceSheet::ABSENT) { + if (CAttendanceSheet::ABSENT === $presence) { $absences++; } } @@ -347,12 +349,12 @@ public function exportToXls(int $id, Request $request): Response $percentage = $totalCalendars > 0 ? round(($absences * 100) / $totalCalendars) : 0; array_splice($row, 3, 0, "$absences/$totalCalendars ($percentage%)"); - $sheet->fromArray($row, null, 'A' . $rowNumber++); + $sheet->fromArray($row, null, 'A'.$rowNumber++); } // Output $writer = new Xls($spreadsheet); - $response = new StreamedResponse(fn() => $writer->save('php://output')); + $response = new StreamedResponse(fn () => $writer->save('php://output')); $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, "attendance-$id.xls" @@ -360,6 +362,7 @@ public function exportToXls(int $id, Request $request): Response $response->headers->set('Content-Type', 'application/vnd.ms-excel'); $response->headers->set('Content-Disposition', $disposition); + return $response; } @@ -373,7 +376,7 @@ public function generateQrCode(int $id, Request $request): Response $resourceNodeId = $attendance->getResourceNode()?->getParent()?->getId(); if (!$resourceNodeId) { - throw new \RuntimeException('Missing resourceNode for course'); + throw new RuntimeException('Missing resourceNode for course'); } $sid = $request->query->get('sid'); @@ -388,18 +391,18 @@ public function generateQrCode(int $id, Request $request): Response } $url = "/resources/attendance/$resourceNodeId/$id/sheet-list?$query"; - $fullUrl = $request->getSchemeAndHttpHost() . $url; + $fullUrl = $request->getSchemeAndHttpHost().$url; $result = Builder::create() ->data($fullUrl) ->size(300) ->margin(10) - ->build(); + ->build() + ; return new Response($result->getString(), 200, ['Content-Type' => $result->getMimeType()]); } - #[Route('/sheet/save', name: 'chamilo_core_attendance_sheet_save', methods: ['POST'])] public function saveAttendanceSheet( Request $request, @@ -554,7 +557,7 @@ public function getStudentDates(int $id): JsonResponse $signatureData = []; foreach ($dates as $item) { - $key = $user->getId() . '-' . $item['id']; + $key = $user->getId().'-'.$item['id']; $attendanceData[$key] = $item['presence']; $signatureData[$key] = $item['signature']; diff --git a/src/CoreBundle/Controller/CatalogueController.php b/src/CoreBundle/Controller/CatalogueController.php index 9975e3995bc..b246d3cbd6e 100644 --- a/src/CoreBundle/Controller/CatalogueController.php +++ b/src/CoreBundle/Controller/CatalogueController.php @@ -19,7 +19,9 @@ use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper; use Chamilo\CoreBundle\ServiceHelper\UserHelper; use Chamilo\CoreBundle\Settings\SettingsManager; +use DateTime; use Doctrine\ORM\EntityManagerInterface; +use ExtraField; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; @@ -175,7 +177,7 @@ public function getCourseExtraFields(SettingsManager $settingsManager): JsonResp return $this->json([]); } - $extraField = new \ExtraField('course'); + $extraField = new ExtraField('course'); $fields = $extraField->get_all(['filter' => 1]); $result = array_map(function ($field) { @@ -199,15 +201,15 @@ public function autoSubscribeCourse(int $courseId, SettingsManager $settings): J return $this->json(['error' => 'Course or user not found'], 400); } - $useAutoSession = $settings->getSetting('session.catalog_course_subscription_in_user_s_session', true) === 'true'; + $useAutoSession = 'true' === $settings->getSetting('session.catalog_course_subscription_in_user_s_session', true); if ($useAutoSession) { $session = new Session(); - $timestamp = (new \DateTime())->format('Ymd_His'); - $sessionTitle = sprintf('%s %s - Session %s', $user->getFirstname(), $user->getLastname(), $timestamp); + $timestamp = (new DateTime())->format('Ymd_His'); + $sessionTitle = \sprintf('%s %s - Session %s', $user->getFirstname(), $user->getLastname(), $timestamp); $session->setTitle($sessionTitle); - $session->setAccessStartDate(new \DateTime()); + $session->setAccessStartDate(new DateTime()); $session->setAccessEndDate(null); $session->setCoachAccessEndDate(null); $session->setDisplayEndDate(null); diff --git a/src/CoreBundle/Controller/CourseController.php b/src/CoreBundle/Controller/CourseController.php index 70f368851a5..aa7d857ffcc 100644 --- a/src/CoreBundle/Controller/CourseController.php +++ b/src/CoreBundle/Controller/CourseController.php @@ -761,7 +761,7 @@ public function createCourse( $title = $courseData['name'] ?? null; $wantedCode = $courseData['code'] ?? null; - $courseLanguage = $courseData['language']['id'] ?? null; + $courseLanguage = $courseData['language'] ?? null; $categoryCode = $courseData['category'] ?? null; $exemplaryContent = $courseData['fillDemoContent'] ?? false; $template = $courseData['template'] ?? ''; diff --git a/src/CoreBundle/Controller/PlatformConfigurationController.php b/src/CoreBundle/Controller/PlatformConfigurationController.php index eca5ecaba43..db70448a54e 100644 --- a/src/CoreBundle/Controller/PlatformConfigurationController.php +++ b/src/CoreBundle/Controller/PlatformConfigurationController.php @@ -20,6 +20,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Throwable; #[Route('/platform-config')] class PlatformConfigurationController extends AbstractController @@ -180,27 +181,28 @@ public function courseSettingsList( private function decodeSettingArray(mixed $setting): array { // Already an array, return as is - if (is_array($setting)) { + if (\is_array($setting)) { return $setting; } // Try to decode JSON string - if (is_string($setting)) { + if (\is_string($setting)) { $json = json_decode($setting, true); - if (is_array($json)) { + if (\is_array($json)) { return $json; } // Try to evaluate PHP-style array string $trimmed = rtrim($setting, ';'); + try { $evaluated = eval("return $trimmed;"); - if (is_array($evaluated)) { + if (\is_array($evaluated)) { return $evaluated; } - } catch (\Throwable $e) { + } catch (Throwable $e) { // Log error and continue - error_log("Failed to eval setting value: " . $e->getMessage()); + error_log('Failed to eval setting value: '.$e->getMessage()); } } diff --git a/src/CoreBundle/Controller/StudentPublicationController.php b/src/CoreBundle/Controller/StudentPublicationController.php index 793fa9e3cde..2ddf642a5cc 100644 --- a/src/CoreBundle/Controller/StudentPublicationController.php +++ b/src/CoreBundle/Controller/StudentPublicationController.php @@ -18,11 +18,13 @@ use Chamilo\CourseBundle\Entity\CStudentPublicationCorrection; use Chamilo\CourseBundle\Repository\CStudentPublicationCorrectionRepository; use Chamilo\CourseBundle\Repository\CStudentPublicationRepository; +use DateTime; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; use Mpdf\Mpdf; use Mpdf\MpdfException; use Mpdf\Output\Destination; +use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Finder\Finder; @@ -33,6 +35,10 @@ use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use Throwable; +use ZipArchive; + +use const PATHINFO_FILENAME; #[Route('/assignments')] class StudentPublicationController extends AbstractController @@ -42,7 +48,6 @@ public function __construct( private readonly CidReqHelper $cidReqHelper ) {} - #[Route('/student', name: 'chamilo_core_assignment_student_list', methods: ['GET'])] public function getStudentAssignments(SerializerInterface $serializer): JsonResponse { @@ -70,7 +75,7 @@ public function getStudentAssignments(SerializerInterface $serializer): JsonResp return new JsonResponse([ 'hydra:member' => $data, - 'hydra:totalItems' => count($data), + 'hydra:totalItems' => \count($data), ]); } @@ -85,7 +90,7 @@ public function getStudentProgress( return new JsonResponse([ 'hydra:member' => $progressList, - 'hydra:totalItems' => count($progressList), + 'hydra:totalItems' => \count($progressList), ]); } @@ -97,7 +102,7 @@ public function getAssignmentSubmissions( CStudentPublicationRepository $repo, Security $security ): JsonResponse { - /* @var User $user */ + /** @var User $user */ $user = $security->getUser(); $page = (int) $request->query->get('page', 1); @@ -195,10 +200,10 @@ public function editSubmission( $description = $data['description'] ?? null; $sendMail = $data['sendMail'] ?? false; - if ($title !== null) { + if (null !== $title) { $submission->setTitle($title); } - if ($description !== null) { + if (null !== $description) { $submission->setDescription($description); } @@ -207,8 +212,8 @@ public function editSubmission( if ($sendMail) { $user = $submission->getUser(); if ($user) { - $messageSubject = sprintf('Feedback updated for "%s"', $submission->getTitle()); - $messageContent = sprintf( + $messageSubject = \sprintf('Feedback updated for "%s"', $submission->getTitle()); + $messageContent = \sprintf( 'There is a new feedback update for your submission "%s". Please check it on the platform.', $submission->getTitle() ); @@ -287,14 +292,14 @@ public function getUnsubmittedUsers( $unsubmitted = array_filter( $studentsArray, - fn ($rel) => !in_array($rel->getUser()->getId(), $submittedUserIds, true) + fn ($rel) => !\in_array($rel->getUser()->getId(), $submittedUserIds, true) ); $data = array_values(array_map(fn ($rel) => $rel->getUser(), $unsubmitted)); return $this->json([ 'hydra:member' => $data, - 'hydra:totalItems' => count($data), + 'hydra:totalItems' => \count($data), ], 200, [], ['groups' => ['user:read']]); } @@ -308,7 +313,7 @@ public function emailUnsubmittedUsers( $course = $this->cidReqHelper->getCourseEntity(); $session = $this->cidReqHelper->getSessionEntity(); - /* @var User $user */ + /** @var User $user */ $user = $security->getUser(); $senderId = $user?->getId(); @@ -320,20 +325,20 @@ public function emailUnsubmittedUsers( $unsubmitted = array_filter( $students->toArray(), - fn ($rel) => !in_array($rel->getUser()->getId(), $submittedUserIds, true) + fn ($rel) => !\in_array($rel->getUser()->getId(), $submittedUserIds, true) ); foreach ($unsubmitted as $rel) { $user = $rel->getUser(); $messageHelper->sendMessageSimple( $user->getId(), - "You have not submitted your assignment", - "Please submit your assignment as soon as possible.", + 'You have not submitted your assignment', + 'Please submit your assignment as soon as possible.', $senderId ); } - return new JsonResponse(['success' => true, 'sent' => count($unsubmitted)]); + return new JsonResponse(['success' => true, 'sent' => \count($unsubmitted)]); } #[Route('/{id}/export/pdf', name: 'chamilo_core_assignment_export_pdf', methods: ['GET'])] @@ -366,7 +371,7 @@ public function exportPdf( try { $mpdf = new Mpdf([ - 'tempDir' => api_get_path(SYS_ARCHIVE_PATH) . 'mpdf/', + 'tempDir' => api_get_path(SYS_ARCHIVE_PATH).'mpdf/', ]); $mpdf->WriteHTML($html); @@ -376,7 +381,7 @@ public function exportPdf( ['Content-Type' => 'application/pdf'] ); } catch (MpdfException $e) { - throw new \RuntimeException('Failed to generate PDF: '.$e->getMessage(), 500, $e); + throw new RuntimeException('Failed to generate PDF: '.$e->getMessage(), 500, $e); } } @@ -390,11 +395,11 @@ public function deleteAllCorrections( $count = 0; - /* @var CStudentPublication $submission */ + /** @var CStudentPublication $submission */ foreach ($submissions as $submission) { $correctionNode = $submission->getCorrection(); - if ($correctionNode !== null) { + if (null !== $correctionNode) { $correctionNode = $em->getRepository(ResourceNode::class)->find($correctionNode->getId()); if ($correctionNode) { $em->remove($correctionNode); @@ -425,11 +430,11 @@ public function downloadAssignmentPackage( } [$submissions] = $repo->findAllSubmissionsByAssignment($assignmentId, 1, 10000); - $zipPath = api_get_path(SYS_ARCHIVE_PATH) . uniqid('assignment_', true) . '.zip'; - $zip = new \ZipArchive(); + $zipPath = api_get_path(SYS_ARCHIVE_PATH).uniqid('assignment_', true).'.zip'; + $zip = new ZipArchive(); - if ($zip->open($zipPath, \ZipArchive::CREATE) !== true) { - throw new \RuntimeException('Cannot create zip archive'); + if (true !== $zip->open($zipPath, ZipArchive::CREATE)) { + throw new RuntimeException('Cannot create zip archive'); } foreach ($submissions as $submission) { @@ -443,9 +448,9 @@ public function downloadAssignmentPackage( $path = $resourceNodeRepository->getFilename($resourceFile); $content = $resourceNodeRepository->getFileSystem()->read($path); - $filename = sprintf('%s_%s_%s', $sentDate, $user->getUsername(), $resourceFile->getOriginalName()); + $filename = \sprintf('%s_%s_%s', $sentDate, $user->getUsername(), $resourceFile->getOriginalName()); $zip->addFromString($filename, $content); - } catch (\Throwable $e) { + } catch (Throwable $e) { continue; } } @@ -453,7 +458,7 @@ public function downloadAssignmentPackage( $zip->close(); - return $this->file($zipPath, $assignment->getTitle() . '.zip')->deleteFileAfterSend(); + return $this->file($zipPath, $assignment->getTitle().'.zip')->deleteFileAfterSend(); } #[Route('/{assignmentId}/upload-corrections-package', name: 'chamilo_core_assignment_upload_corrections_package', methods: ['POST'])] @@ -466,15 +471,15 @@ public function uploadCorrectionsPackage( TranslatorInterface $translator ): JsonResponse { $file = $request->files->get('file'); - if (!$file || $file->getClientOriginalExtension() !== 'zip') { + if (!$file || 'zip' !== $file->getClientOriginalExtension()) { return new JsonResponse(['error' => 'Invalid file'], 400); } $folder = uniqid('corrections_', true); - $destinationDir = api_get_path(SYS_ARCHIVE_PATH) . $folder; + $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder; mkdir($destinationDir, 0777, true); - $zip = new \ZipArchive(); + $zip = new ZipArchive(); $zip->open($file->getPathname()); $zip->extractTo($destinationDir); $zip->close(); @@ -487,7 +492,7 @@ public function uploadCorrectionsPackage( $username = $submission->getUser()?->getUsername() ?? 'unknown'; $title = $this->cleanFilename($submission->getTitle() ?? ''); $title = preg_replace('/_[a-f0-9]{10,}$/', '', pathinfo($title, PATHINFO_FILENAME)); - $key = sprintf('%s_%s_%s', $date, $username, $title); + $key = \sprintf('%s_%s_%s', $date, $username, $title); $matchMap[$key] = $submission; } @@ -505,7 +510,6 @@ public function uploadCorrectionsPackage( $matched = false; foreach ($matchMap as $prefix => $submission) { if ($nameOnly === $prefix) { - if ($submission->getCorrection()) { $em->remove($submission->getCorrection()); $em->flush(); @@ -529,12 +533,13 @@ public function uploadCorrectionsPackage( $submission->setExtensions($filename); $submission->setDescription('Correction uploaded'); $submission->setQualification(0); - $submission->setDateOfQualification(new \DateTime()); + $submission->setDateOfQualification(new DateTime()); $submission->setAccepted(true); $em->persist($submission); $uploaded++; $matched = true; + break; } } @@ -549,7 +554,7 @@ public function uploadCorrectionsPackage( return new JsonResponse([ 'success' => true, 'uploaded' => $uploaded, - 'skipped' => count($skipped), + 'skipped' => \count($skipped), 'skipped_files' => $skipped, ]); } @@ -563,6 +568,7 @@ private function cleanFilename(string $name): string $name = preg_replace('/\s+/', '_', $name); $name = preg_replace('/[^\w\-\.]/u', '', $name); $name = preg_replace('/_+/', '_', $name); + return trim($name, '_'); } } diff --git a/src/CoreBundle/Entity/CourseRelUser.php b/src/CoreBundle/Entity/CourseRelUser.php index 8ab7ce65cf4..675a995bfaa 100644 --- a/src/CoreBundle/Entity/CourseRelUser.php +++ b/src/CoreBundle/Entity/CourseRelUser.php @@ -53,7 +53,7 @@ #[ApiFilter(PartialSearchOrFilter::class, properties: [ 'user.username', 'user.firstname', - 'user.lastname' + 'user.lastname', ])] class CourseRelUser implements Stringable { diff --git a/src/CoreBundle/EventSubscriber/LocaleSubscriber.php b/src/CoreBundle/EventSubscriber/LocaleSubscriber.php index 2b2a74faa26..7922a393d1e 100644 --- a/src/CoreBundle/EventSubscriber/LocaleSubscriber.php +++ b/src/CoreBundle/EventSubscriber/LocaleSubscriber.php @@ -25,8 +25,7 @@ public function __construct( private SettingsManager $settingsManager, private ParameterBagInterface $parameterBag, private SettingsCourseManager $courseSettingsManager - ) { - } + ) {} public function onKernelRequest(RequestEvent $event): void { @@ -74,7 +73,7 @@ public function getCurrentLanguage(Request $request): string $courseLocale = $course->getCourseLanguage(); $this->courseSettingsManager->setCourse($course); - if ($this->courseSettingsManager->getCourseSettingValue('show_course_in_user_language') === '1' && $userLocale) { + if ('1' === $this->courseSettingsManager->getCourseSettingValue('show_course_in_user_language') && $userLocale) { $localeList['course_lang'] = $userLocale; } elseif ($courseLocale) { $localeList['course_lang'] = $courseLocale; @@ -88,11 +87,11 @@ public function getCurrentLanguage(Request $request): string // 5. Resolve locale based on configured language priorities foreach ([ - 'language_priority_1', - 'language_priority_2', - 'language_priority_3', - 'language_priority_4', - ] as $settingKey) { + 'language_priority_1', + 'language_priority_2', + 'language_priority_3', + 'language_priority_4', + ] as $settingKey) { $priority = $this->settingsManager->getSetting("language.$settingKey"); if (!empty($priority) && !empty($localeList[$priority])) { return $localeList[$priority]; diff --git a/src/CoreBundle/Filter/ParentNullFilter.php b/src/CoreBundle/Filter/ParentNullFilter.php index 1491ad3ca98..7b7cdc2051c 100644 --- a/src/CoreBundle/Filter/ParentNullFilter.php +++ b/src/CoreBundle/Filter/ParentNullFilter.php @@ -34,7 +34,7 @@ public function getDescription(string $resourceClass): array 'property' => $property, 'type' => 'bool', 'required' => false, - 'description' => sprintf('Filter on %s: IS NULL or IS NOT NULL', $property), + 'description' => \sprintf('Filter on %s: IS NULL or IS NOT NULL', $property), ]; } @@ -43,14 +43,14 @@ public function getDescription(string $resourceClass): array protected function filterProperty( string $property, - $value, + $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = [], ): void { - if (!array_key_exists($property, $this->properties)) { + if (!\array_key_exists($property, $this->properties)) { return; } @@ -59,16 +59,16 @@ protected function filterProperty( if (str_contains($property, '.')) { $parts = explode('.', $property); $joinAlias = $queryNameGenerator->generateJoinAlias($parts[0]); - $queryBuilder->leftJoin(sprintf('%s.%s', $alias, $parts[0]), $joinAlias); - $field = sprintf('%s.%s', $joinAlias, $parts[1]); + $queryBuilder->leftJoin(\sprintf('%s.%s', $alias, $parts[0]), $joinAlias); + $field = \sprintf('%s.%s', $joinAlias, $parts[1]); } else { - $field = sprintf('%s.%s', $alias, $property); + $field = \sprintf('%s.%s', $alias, $property); } - if ($value === 'true' || $value === true || $value === '1' || $value === 1) { - $queryBuilder->andWhere(sprintf('%s IS NOT NULL', $field)); + if ('true' === $value || true === $value || '1' === $value || 1 === $value) { + $queryBuilder->andWhere(\sprintf('%s IS NOT NULL', $field)); } else { - $queryBuilder->andWhere(sprintf('%s IS NULL', $field)); + $queryBuilder->andWhere(\sprintf('%s IS NULL', $field)); } } } diff --git a/src/CoreBundle/Filter/PartialSearchOrFilter.php b/src/CoreBundle/Filter/PartialSearchOrFilter.php index 0e82695ac48..a556253ef95 100644 --- a/src/CoreBundle/Filter/PartialSearchOrFilter.php +++ b/src/CoreBundle/Filter/PartialSearchOrFilter.php @@ -16,7 +16,7 @@ class PartialSearchOrFilter extends AbstractFilter { protected function filterProperty( string $property, - $value, + $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, @@ -32,8 +32,8 @@ protected function filterProperty( } $alias = $queryBuilder->getRootAliases()[0]; - $valueParameter = ':' . $queryNameGenerator->generateParameterName($property); - $queryBuilder->setParameter($valueParameter, '%' . $value . '%'); + $valueParameter = ':'.$queryNameGenerator->generateParameterName($property); + $queryBuilder->setParameter($valueParameter, '%'.$value.'%'); $ors = []; @@ -41,10 +41,10 @@ protected function filterProperty( // Detect if field is a relation (e.g. "user.username") if (str_contains($field, '.')) { [$relation, $subField] = explode('.', $field, 2); - $joinAlias = $relation . '_alias'; + $joinAlias = $relation.'_alias'; // Ensure the join is only added once - if (!in_array($joinAlias, $queryBuilder->getAllAliases(), true)) { + if (!\in_array($joinAlias, $queryBuilder->getAllAliases(), true)) { $queryBuilder->leftJoin("$alias.$relation", $joinAlias); } diff --git a/src/CoreBundle/Form/ExtraFieldType.php b/src/CoreBundle/Form/ExtraFieldType.php index 44734d15452..5d1ba90566e 100644 --- a/src/CoreBundle/Form/ExtraFieldType.php +++ b/src/CoreBundle/Form/ExtraFieldType.php @@ -223,6 +223,8 @@ public function buildForm(FormBuilderInterface $builder, array $options): void case \ExtraField::FIELD_TYPE_RADIO: case \ExtraField::FIELD_TYPE_SELECT: + $defaultOptions['attr']['class'] = 'p-select p-component p-inputwrapper p-inputwrapper-filled'; + //no break case \ExtraField::FIELD_TYPE_SELECT_MULTIPLE: if (empty($value)) { $defaultOptions['data'] = null; diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20250429144100.php b/src/CoreBundle/Migrations/Schema/V200/Version20250429144100.php index 6f15e208fb3..0021221e549 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20250429144100.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20250429144100.php @@ -7,12 +7,15 @@ namespace Chamilo\CoreBundle\Migrations\Schema\V200; use Chamilo\CoreBundle\Entity\Asset; +use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; use Chamilo\CoreBundle\Repository\AssetRepository; use Chamilo\CourseBundle\Entity\CLp; -use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo; use Chamilo\CourseBundle\Repository\CLpRepository; use Doctrine\DBAL\Schema\Schema; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; use Symfony\Component\HttpFoundation\File\UploadedFile; +use ZipArchive; final class Version20250429144100 extends AbstractMigrationChamilo { @@ -25,8 +28,8 @@ public function up(Schema $schema): void { $this->log('Starting SCORM migration...'); - $assetRepo = $this->container->get( AssetRepository::class); - $lpRepo = $this->container->get(CLpRepository::class);; + $assetRepo = $this->container->get(AssetRepository::class); + $lpRepo = $this->container->get(CLpRepository::class); $courses = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c')->toIterable(); @@ -43,10 +46,12 @@ public function up(Schema $schema): void ->setParameter('type', CLp::SCORM_TYPE) ->setParameter('course', $course) ->getQuery() - ->getResult(); + ->getResult() + ; if (empty($scorms)) { $this->log("No SCORMs found for course $courseDir"); + continue; } @@ -60,6 +65,7 @@ public function up(Schema $schema): void if (!is_dir($folderPath)) { $this->log("SCORM folder not found: $folderPath"); + continue; } @@ -73,6 +79,7 @@ public function up(Schema $schema): void if (!file_exists($tmpZipPath)) { $this->log("Failed to create zip: $tmpZipPath"); + continue; } @@ -82,14 +89,15 @@ public function up(Schema $schema): void $asset->setCategory(Asset::SCORM) ->setTitle($zipName) ->setFile($file) - ->setCompressed(true); + ->setCompressed(true) + ; $assetRepo->update($asset); - $this->log("Asset created: id=".$asset->getId()); + $this->log('Asset created: id='.$asset->getId()); $assetRepo->unZipFile($asset, basename($path)); - $this->log("Asset unzipped for: ".$asset->getTitle()); + $this->log('Asset unzipped for: '.$asset->getTitle()); $lp->setAsset($asset); $lp->setPath(basename($path).'/.'); @@ -105,20 +113,21 @@ public function up(Schema $schema): void private function zipFolder(string $folderPath, string $zipPath): void { - $zip = new \ZipArchive(); - if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { + $zip = new ZipArchive(); + if (true !== $zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE)) { $this->log("Cannot create ZIP file: $zipPath"); + return; } - $files = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::LEAVES_ONLY + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($folderPath, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $file) { $filePath = $file->getRealPath(); - $relativePath = substr($filePath, strlen($folderPath) + 1); + $relativePath = substr($filePath, \strlen($folderPath) + 1); $zip->addFile($filePath, $relativePath); } @@ -127,7 +136,7 @@ private function zipFolder(string $folderPath, string $zipPath): void private function log(string $message): void { - error_log('[SCORM Migration] ' . $message); + error_log('[SCORM Migration] '.$message); } public function down(Schema $schema): void {} diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20250501000100.php b/src/CoreBundle/Migrations/Schema/V200/Version20250501000100.php index 5ce36b65f1f..0d073b29a72 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20250501000100.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20250501000100.php @@ -30,8 +30,9 @@ public function up(Schema $schema): void $root = $kernel->getProjectDir(); $courses = $this->entityManager - ->createQuery('SELECT c FROM Chamilo\\CoreBundle\\Entity\\Course c') - ->toIterable(); + ->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c') + ->toIterable() + ; foreach ($courses as $course) { $courseDir = $course->getDirectory(); @@ -46,7 +47,8 @@ public function up(Schema $schema): void ->setParameter('course', $course) ->setParameter('file', 'file') ->getQuery() - ->getResult(); + ->getResult() + ; foreach ($publications as $publication) { if (!$publication instanceof CStudentPublication || !$publication->getResourceNode()) { @@ -99,7 +101,8 @@ public function up(Schema $schema): void ->andWhere('c.file IS NOT NULL') ->setParameter('course', $course) ->getQuery() - ->getResult(); + ->getResult() + ; foreach ($comments as $comment) { if (!$comment instanceof CStudentPublicationComment || !$comment->getResourceNode()) { @@ -139,6 +142,7 @@ private function resourceNodeHasFile($resourceNode, string $filename): bool return true; } } + return false; } } diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20250527101500.php b/src/CoreBundle/Migrations/Schema/V200/Version20250527101500.php index a3af015f909..79675e53a69 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20250527101500.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20250527101500.php @@ -11,15 +11,11 @@ class Version20250527101500 extends AbstractMigrationChamilo { - public function getDescription(): string { return 'Add index on the resource_type.title column'; } - /** - * @inheritDoc - */ public function up(Schema $schema): void { $this->addSql('CREATE INDEX idx_title ON resource_type (title);'); diff --git a/src/CoreBundle/Repository/AccessUrlRelPluginRepository.php b/src/CoreBundle/Repository/AccessUrlRelPluginRepository.php index b0e2e34ded2..bffb6aee28f 100644 --- a/src/CoreBundle/Repository/AccessUrlRelPluginRepository.php +++ b/src/CoreBundle/Repository/AccessUrlRelPluginRepository.php @@ -29,6 +29,7 @@ public function findOneByPluginName(string $pluginTitle, int $accessUrlId): ?Acc ->setParameter('pluginTitle', $pluginTitle) ->setParameter('accessUrlId', $accessUrlId) ->getQuery() - ->getOneOrNullResult(); + ->getOneOrNullResult() + ; } } diff --git a/src/CoreBundle/Repository/ExtraFieldValuesRepository.php b/src/CoreBundle/Repository/ExtraFieldValuesRepository.php index de488500466..71fabed63a9 100644 --- a/src/CoreBundle/Repository/ExtraFieldValuesRepository.php +++ b/src/CoreBundle/Repository/ExtraFieldValuesRepository.php @@ -209,7 +209,8 @@ public function getValueByVariableAndItem(string $variable, int $itemId, int $it ->setParameter('variable', $variable) ->setParameter('itemType', $itemType) ->setParameter('itemId', $itemId) - ->setMaxResults(1); + ->setMaxResults(1) + ; return $qb->getQuery()->getOneOrNullResult(); } @@ -229,6 +230,7 @@ public function getByHandlerAndFieldId(int $itemId, int $fieldId, int $itemType, 'item_type' => $itemType, ]) ->getQuery() - ->getResult(); + ->getResult() + ; } } diff --git a/src/CoreBundle/Resources/views/Layout/form-theme.html.twig b/src/CoreBundle/Resources/views/Layout/form-theme.html.twig index 7d0f25ce374..ade9e6d8699 100644 --- a/src/CoreBundle/Resources/views/Layout/form-theme.html.twig +++ b/src/CoreBundle/Resources/views/Layout/form-theme.html.twig @@ -33,7 +33,7 @@ vendor/symfony/twig-bridge/Resources/views/Form/tailwind_2_layout.html.twig #} {%- endblock form_widget_simple -%} {%- block choice_widget_collapsed -%} - {%- set attr = attr|merge({ class: attr.class|default('p-dropdown p-component p-inputwrapper p-inputwrapper-filled') }) -%} + {%- set attr = attr|merge({ class: attr.class|default('p-select p-component p-inputwrapper p-inputwrapper-filled') }) -%} {{- parent() -}} {%- endblock -%} diff --git a/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php b/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php index fdbdbc4a283..41be30d5358 100644 --- a/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php +++ b/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php @@ -134,7 +134,8 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $ if (\in_array($question->getType(), [6, 7, 8], true)) { // HOT_SPOT, HOT_SPOT_ORDER, HOT_SPOT_DELINEATION $rel = $this->entityManager ->getRepository(CQuizRelQuestion::class) - ->findOneBy(['question' => $question]); + ->findOneBy(['question' => $question]) + ; if ($rel && $rel->getQuiz()) { $quiz = $rel->getQuiz(); @@ -146,7 +147,7 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $ } } - // no break + // no break case self::EDIT: break; } @@ -186,17 +187,19 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $ } } - if ($resourceNode->getResourceType()->getTitle() === 'files') { + if ('files' === $resourceNode->getResourceType()->getTitle()) { $document = $this->entityManager ->getRepository(CDocument::class) - ->findOneBy(['resourceNode' => $resourceNode]); + ->findOneBy(['resourceNode' => $resourceNode]) + ; if ($document) { $exists = $this->entityManager ->getRepository(CStudentPublicationRelDocument::class) - ->findOneBy(['document' => $document]); + ->findOneBy(['document' => $document]) + ; - if ($exists !== null) { + if (null !== $exists) { return true; } } diff --git a/src/CoreBundle/ServiceHelper/PluginServiceHelper.php b/src/CoreBundle/ServiceHelper/PluginServiceHelper.php index 048acde4b13..1c3a9fa31f2 100644 --- a/src/CoreBundle/ServiceHelper/PluginServiceHelper.php +++ b/src/CoreBundle/ServiceHelper/PluginServiceHelper.php @@ -7,6 +7,7 @@ namespace Chamilo\CoreBundle\ServiceHelper; use Chamilo\CoreBundle\Repository\AccessUrlRelPluginRepository; +use Event; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; class PluginServiceHelper @@ -20,7 +21,7 @@ public function __construct( public function loadLegacyPlugin(string $pluginName): ?object { $projectDir = $this->parameterBag->get('kernel.project_dir'); - $pluginPath = $projectDir . '/public/plugin/' . $pluginName . '/src/' . $pluginName . '.php'; + $pluginPath = $projectDir.'/public/plugin/'.$pluginName.'/src/'.$pluginName.'.php'; $pluginClass = $pluginName; if (!file_exists($pluginPath)) { @@ -69,7 +70,7 @@ public function shouldBlockAccessByPositioning(?int $userId, int $courseId, ?int $plugin = $this->loadLegacyPlugin('Positioning'); - if (!$plugin || $plugin->get('block_course_if_initial_exercise_not_attempted') !== 'true') { + if (!$plugin || 'true' !== $plugin->get('block_course_if_initial_exercise_not_attempted')) { return false; } @@ -79,7 +80,7 @@ public function shouldBlockAccessByPositioning(?int $userId, int $courseId, ?int return false; } - $results = \Event::getExerciseResultsByUser( + $results = Event::getExerciseResultsByUser( $userId, (int) $initialData['exercise_id'], $courseId, diff --git a/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php b/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php index 902727aca94..ad7f4f3ca46 100644 --- a/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php +++ b/src/CoreBundle/State/CStudentPublicationPostStateProcessor.php @@ -57,7 +57,7 @@ public function process( /** @var User $currentUser */ $currentUser = $this->security->getUser(); - $isUpdate = $publication->getIid() !== null; + $isUpdate = null !== $publication->getIid(); if (!$assignment) { $assignment = new CStudentPublicationAssignment(); @@ -68,21 +68,21 @@ public function process( $payload = $context['request']->toArray(); - if (array_key_exists('qualification', $payload)) { + if (\array_key_exists('qualification', $payload)) { $publication->setQualification((float) $payload['qualification']); $user = $this->security->getUser(); if ($user instanceof User) { $publication->setQualificatorId($user->getId()); - $publication->setDateOfQualification(new \DateTime()); + $publication->setDateOfQualification(new DateTime()); } } if (isset($payload['expiresOn'])) { - $assignment->setExpiresOn(new \DateTime($payload['expiresOn'])); + $assignment->setExpiresOn(new DateTime($payload['expiresOn'])); } if (isset($payload['endsOn'])) { - $assignment->setEndsOn(new \DateTime($payload['endsOn'])); + $assignment->setEndsOn(new DateTime($payload['endsOn'])); } if (!$isUpdate || $publication->getQualification() > 0) { @@ -96,12 +96,13 @@ public function process( $assignment->setEventCalendarId(0); } - if ($assignment->getIid() !== null) { + if (null !== $assignment->getIid()) { $publication->setHasProperties($assignment->getIid()); } $publication ->setViewProperties(true) - ->setUser($currentUser); + ->setUser($currentUser) + ; $this->entityManager->flush(); diff --git a/src/CoreBundle/State/CToolStateProvider.php b/src/CoreBundle/State/CToolStateProvider.php index 2751d6218c8..a3ba98b3af1 100644 --- a/src/CoreBundle/State/CToolStateProvider.php +++ b/src/CoreBundle/State/CToolStateProvider.php @@ -20,6 +20,7 @@ use Chamilo\CoreBundle\Traits\CourseFromRequestTrait; use Chamilo\CourseBundle\Entity\CTool; use Doctrine\ORM\EntityManagerInterface; +use Event; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpFoundation\RequestStack; @@ -62,10 +63,10 @@ public function provide(Operation $operation, array $uriVariables = [], array $c $isAllowToEdit = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')); $isAllowToEditBack = $isAllowToEdit; $isAllowToSessionEdit = $user && ( - $user->hasRole('ROLE_ADMIN') || - $user->hasRole('ROLE_CURRENT_COURSE_TEACHER') || - $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') - ); + $user->hasRole('ROLE_ADMIN') + || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER') + || $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER') + ); $allowVisibilityInSession = $this->settingsManager->getSetting('course.allow_edit_tool_visibility_in_session'); $session = $this->getSession(); @@ -149,7 +150,7 @@ private function shouldRestrictToPositioningOnly(?User $user, int $courseId, ?in return [false, null]; } - $results = \Event::getExerciseResultsByUser( + $results = Event::getExerciseResultsByUser( $user->getId(), (int) $initialData['exercise_id'], $courseId, diff --git a/src/CoreBundle/State/PublicCatalogueCourseStateProvider.php b/src/CoreBundle/State/PublicCatalogueCourseStateProvider.php index 932532d7383..a098bd05a6c 100644 --- a/src/CoreBundle/State/PublicCatalogueCourseStateProvider.php +++ b/src/CoreBundle/State/PublicCatalogueCourseStateProvider.php @@ -28,27 +28,27 @@ public function __construct( private TokenStorageInterface $tokenStorage ) {} - public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|null|object + public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|object|null { $user = $this->tokenStorage->getToken()?->getUser(); $isAuthenticated = \is_object($user); if (!$isAuthenticated) { - $showCatalogue = $this->settingsManager->getSetting('course.course_catalog_published', true) !== 'false'; + $showCatalogue = 'false' !== $this->settingsManager->getSetting('course.course_catalog_published', true); if (!$showCatalogue) { return []; } } - $onlyShowMatching = $this->settingsManager->getSetting('course.show_courses_in_catalogue', true) === 'true'; - $onlyShowCoursesWithCategory = $this->settingsManager->getSetting('course.courses_catalogue_show_only_category', true) === 'true'; + $onlyShowMatching = 'true' === $this->settingsManager->getSetting('course.show_courses_in_catalogue', true); + $onlyShowCoursesWithCategory = 'true' === $this->settingsManager->getSetting('course.courses_catalogue_show_only_category', true); $request = $this->requestStack->getCurrentRequest(); if (!$request) { return []; } - $host = $request->getSchemeAndHttpHost() . '/'; + $host = $request->getSchemeAndHttpHost().'/'; $accessUrl = $this->accessUrlRepository->findOneBy(['url' => $host]) ?? $this->accessUrlRepository->find(1); $courses = $this->courseRepository->createQueryBuilder('c') ->innerJoin('c.urls', 'url_rel') @@ -58,7 +58,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c ->setParameter('visibilities', [Course::OPEN_WORLD, Course::OPEN_PLATFORM]) ->orderBy('c.title', 'ASC') ->getQuery() - ->getResult(); + ->getResult() + ; if (!$onlyShowMatching && !$onlyShowCoursesWithCategory) { return $courses; @@ -75,7 +76,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c $course->getId(), ExtraField::COURSE_FIELD_TYPE ); - $passesExtraField = $value?->getFieldValue() === '1'; + $passesExtraField = '1' === $value?->getFieldValue(); } if ($onlyShowCoursesWithCategory) { diff --git a/src/CoreBundle/Tool/ToolChain.php b/src/CoreBundle/Tool/ToolChain.php index 09f4ca7d41c..b9e436d935e 100644 --- a/src/CoreBundle/Tool/ToolChain.php +++ b/src/CoreBundle/Tool/ToolChain.php @@ -15,6 +15,7 @@ use Chamilo\CoreBundle\Settings\SettingsManager; use Chamilo\CourseBundle\Entity\CTool; use Doctrine\ORM\EntityManagerInterface; +use InvalidArgumentException; use Symfony\Bundle\SecurityBundle\Security; /** @@ -210,12 +211,12 @@ public function getTools(): iterable public function getToolFromName(string $title): AbstractTool { foreach ($this->handlerCollection->getCollection() as $handler) { - if (strcasecmp($handler->getTitle(), $title) === 0) { + if (0 === strcasecmp($handler->getTitle(), $title)) { return $handler; } } - throw new \InvalidArgumentException("Tool handler not found for title: $title"); + throw new InvalidArgumentException("Tool handler not found for title: $title"); } /*public function getToolFromEntity(string $entityClass): AbstractTool diff --git a/src/CourseBundle/Entity/CAttendance.php b/src/CourseBundle/Entity/CAttendance.php index dc767703c8f..2bd49f3c24b 100644 --- a/src/CourseBundle/Entity/CAttendance.php +++ b/src/CourseBundle/Entity/CAttendance.php @@ -82,7 +82,7 @@ ], normalizationContext: [ 'groups' => ['attendance:read', 'resource_node:read', 'resource_link:read'], - 'enable_max_depth' => true + 'enable_max_depth' => true, ], denormalizationContext: ['groups' => ['attendance:write']], paginationEnabled: true, @@ -302,12 +302,14 @@ public function getDoneCalendars(): int { return $this->calendars ->filter(fn (CAttendanceCalendar $calendar) => $calendar->getDoneAttendance()) - ->count(); + ->count() + ; } public function setDoneCalendars(?int $count): self { $this->doneCalendars = $count; + return $this; } diff --git a/src/CourseBundle/Entity/CStudentPublication.php b/src/CourseBundle/Entity/CStudentPublication.php index 33129d869bb..7da0336fb7b 100644 --- a/src/CourseBundle/Entity/CStudentPublication.php +++ b/src/CourseBundle/Entity/CStudentPublication.php @@ -489,8 +489,9 @@ public function getCorrectionTitle(): ?string public function getChildFileCount(): int { return $this->children - ->filter(fn (self $child) => $child->getFiletype() === 'file' && $child->getActive() !== 2) - ->count(); + ->filter(fn (self $child) => 'file' === $child->getFiletype() && 2 !== $child->getActive()) + ->count() + ; } /** @@ -663,7 +664,8 @@ public function getUniqueStudentAttemptsTotal(): int } return $accumulator; - }, 0); + }, 0) + ; return $reduce ?: 0; } diff --git a/src/CourseBundle/Entity/CStudentPublicationComment.php b/src/CourseBundle/Entity/CStudentPublicationComment.php index 2dbe26a948e..19b4ce7322a 100644 --- a/src/CourseBundle/Entity/CStudentPublicationComment.php +++ b/src/CourseBundle/Entity/CStudentPublicationComment.php @@ -8,7 +8,9 @@ use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Post; use Chamilo\CoreBundle\Controller\Api\CreateStudentPublicationCommentAction; use Chamilo\CoreBundle\Entity\AbstractResource; use Chamilo\CoreBundle\Entity\ResourceInterface; @@ -18,10 +20,8 @@ use DateTime; use Doctrine\ORM\Mapping as ORM; use Stringable; -use Symfony\Component\Uid\Uuid; -use ApiPlatform\Metadata\ApiResource; -use ApiPlatform\Metadata\Post; use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Uid\Uuid; #[ApiResource( operations: [ @@ -143,8 +143,8 @@ public function getResourceName(): string { $comment = trim((string) $this->getComment()); - if ($comment === '') { - return 'comment-' . (new \DateTime())->format('Ymd-His'); + if ('' === $comment) { + return 'comment-'.(new DateTime())->format('Ymd-His'); } $text = strip_tags($comment); diff --git a/src/CourseBundle/Entity/CStudentPublicationRelDocument.php b/src/CourseBundle/Entity/CStudentPublicationRelDocument.php index 2e0ed34afe0..62ce822fdb8 100644 --- a/src/CourseBundle/Entity/CStudentPublicationRelDocument.php +++ b/src/CourseBundle/Entity/CStudentPublicationRelDocument.php @@ -8,21 +8,20 @@ use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Metadata\ApiFilter; -use Doctrine\ORM\Mapping as ORM; use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Post; -use ApiPlatform\Metadata\Get; -use ApiPlatform\Metadata\Delete; +use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; - #[ApiResource( operations: [ new Get(), new Delete(), new Post(), - new GetCollection() + new GetCollection(), ], normalizationContext: ['groups' => ['student_publication_rel_document:read']], denormalizationContext: ['groups' => ['student_publication_rel_document:write']] diff --git a/src/CourseBundle/Entity/CStudentPublicationRelUser.php b/src/CourseBundle/Entity/CStudentPublicationRelUser.php index 23d52616366..4209be85c89 100644 --- a/src/CourseBundle/Entity/CStudentPublicationRelUser.php +++ b/src/CourseBundle/Entity/CStudentPublicationRelUser.php @@ -14,8 +14,8 @@ use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Post; use Chamilo\CoreBundle\Entity\User; -use Symfony\Component\Serializer\Annotation\Groups; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; #[ORM\Table(name: 'c_student_publication_rel_user')] #[ORM\Entity] @@ -24,7 +24,7 @@ new Get(security: "is_granted('ROLE_USER')"), new GetCollection(security: "is_granted('ROLE_USER')"), new Post(security: "is_granted('ROLE_TEACHER') or is_granted('ROLE_SESSION_MANAGER')"), - new Delete(security: "is_granted('ROLE_TEACHER') or is_granted('ROLE_SESSION_MANAGER')") + new Delete(security: "is_granted('ROLE_TEACHER') or is_granted('ROLE_SESSION_MANAGER')"), ], normalizationContext: ['groups' => ['student_publication_rel_user:read']], denormalizationContext: ['groups' => ['student_publication_rel_user:write']], diff --git a/src/CourseBundle/Repository/CAttendanceCalendarRepository.php b/src/CourseBundle/Repository/CAttendanceCalendarRepository.php index eb69085fa5e..bb73393aa21 100644 --- a/src/CourseBundle/Repository/CAttendanceCalendarRepository.php +++ b/src/CourseBundle/Repository/CAttendanceCalendarRepository.php @@ -9,7 +9,6 @@ use Chamilo\CoreBundle\Repository\ResourceRepository; use Chamilo\CourseBundle\Entity\CAttendanceCalendar; use Chamilo\CourseBundle\Entity\CAttendanceResultComment; -use Chamilo\CourseBundle\Entity\CAttendanceSheet; use DateTime; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; @@ -134,7 +133,7 @@ public function findAttendanceWithData(int $attendanceId): array return [ 'id' => $calendar->getIid(), 'label' => $calendar->getDateTime()->format('M d, Y - h:i A'), - 'done' => $calendar->getDoneAttendance() === true, + 'done' => true === $calendar->getDoneAttendance(), ]; }, $calendars); diff --git a/src/CourseBundle/Repository/CQuizRepository.php b/src/CourseBundle/Repository/CQuizRepository.php index 8ea983e5fe3..89f8ccb95e3 100644 --- a/src/CourseBundle/Repository/CQuizRepository.php +++ b/src/CourseBundle/Repository/CQuizRepository.php @@ -161,7 +161,7 @@ public function findQuizzesUsingQuestion(int $questionId, int $excludeQuizId = 0 $qb = $this->getEntityManager()->createQueryBuilder(); $qb->select('quiz', 'rn', 'rl', 'course', 'session') - ->from(CQuiz::class, 'quiz') + ->from(CQuiz::class, 'quiz') ->innerJoin('quiz.questions', 'rel') ->innerJoin('quiz.resourceNode', 'rn') ->leftJoin('rn.resourceLinks', 'rl') @@ -169,11 +169,13 @@ public function findQuizzesUsingQuestion(int $questionId, int $excludeQuizId = 0 ->leftJoin('rl.session', 'session') ->where('rel.question = :questionId') ->setParameter('questionId', $questionId) - ->groupBy('quiz.iid'); + ->groupBy('quiz.iid') + ; if ($excludeQuizId > 0) { $qb->andWhere('quiz.iid != :excludeQuizId') - ->setParameter('excludeQuizId', $excludeQuizId); + ->setParameter('excludeQuizId', $excludeQuizId) + ; } return $qb->getQuery()->getResult(); diff --git a/src/CourseBundle/Repository/CStudentPublicationRepository.php b/src/CourseBundle/Repository/CStudentPublicationRepository.php index aef0a073c08..c2170f84bcb 100644 --- a/src/CourseBundle/Repository/CStudentPublicationRepository.php +++ b/src/CourseBundle/Repository/CStudentPublicationRepository.php @@ -155,9 +155,9 @@ public function findVisibleAssignmentsForStudent(Course $course, ?Session $sessi $qb = $this->createQueryBuilder('resource') ->select('resource') - ->addSelect('(SELECT COUNT(comment.iid) FROM ' . CStudentPublicationComment::class . ' comment WHERE comment.publication = resource) AS commentsCount') - ->addSelect('(SELECT COUNT(c1.iid) FROM ' . CStudentPublication::class . ' c1 WHERE c1.publicationParent = resource AND c1.extensions IS NOT NULL AND c1.extensions <> \'\') AS correctionsCount') - ->addSelect('(SELECT MAX(c2.sentDate) FROM ' . CStudentPublication::class . ' c2 WHERE c2.publicationParent = resource) AS lastUpload') + ->addSelect('(SELECT COUNT(comment.iid) FROM '.CStudentPublicationComment::class.' comment WHERE comment.publication = resource) AS commentsCount') + ->addSelect('(SELECT COUNT(c1.iid) FROM '.CStudentPublication::class.' c1 WHERE c1.publicationParent = resource AND c1.extensions IS NOT NULL AND c1.extensions <> \'\') AS correctionsCount') + ->addSelect('(SELECT MAX(c2.sentDate) FROM '.CStudentPublication::class.' c2 WHERE c2.publicationParent = resource) AS lastUpload') ->join('resource.resourceNode', 'rn') ->join('rn.resourceLinks', 'rl') ->leftJoin(CStudentPublicationRelUser::class, 'rel', 'WITH', 'rel.publication = resource AND rel.user = :userId') @@ -169,11 +169,13 @@ public function findVisibleAssignmentsForStudent(Course $course, ?Session $sessi ->andWhere('rl.course = :course') ->setParameter('course', $course) ->setParameter('userId', $userId) - ->orderBy('resource.sentDate', 'DESC'); + ->orderBy('resource.sentDate', 'DESC') + ; if ($session) { $qb->andWhere('rl.session = :session') - ->setParameter('session', $session); + ->setParameter('session', $session) + ; } else { $qb->andWhere('rl.session IS NULL'); } @@ -204,7 +206,8 @@ public function findStudentProgressByCourse(Course $course, ?Session $session): ->andWhere('sp.filetype = :filetype') ->andWhere('sp.publicationParent IS NULL') ->setParameter('course', $course) - ->setParameter('filetype', 'folder'); + ->setParameter('filetype', 'folder') + ; if ($session) { $qb->setParameter('session', $session); @@ -247,7 +250,8 @@ public function findStudentProgressByCourse(Course $course, ?Session $session): ->andWhere('sp.publicationParent IN (:workIds)') ->andWhere('sp.active IN (0, 1)') ->setParameter('user', $user) - ->setParameter('workIds', $workIds); + ->setParameter('workIds', $workIds) + ; $submissionCount = (int) $qb->getQuery()->getSingleScalarResult(); @@ -256,7 +260,7 @@ public function findStudentProgressByCourse(Course $course, ?Session $session): 'firstname' => $user->getFirstname(), 'lastname' => $user->getLastname(), 'submissions' => $submissionCount, - 'totalAssignments' => count($workIds), + 'totalAssignments' => \count($workIds), ]; } @@ -282,20 +286,22 @@ public function findAssignmentSubmissionsPaginated( ->andWhere('resourceLink.visibility = :publishedVisibility') ->setParameter('assignmentId', $assignmentId) ->setParameter('filetype', 'file') - ->setParameter('publishedVisibility', 2); + ->setParameter('publishedVisibility', 2) + ; foreach ($order as $field => $direction) { - $qb->addOrderBy('submission.' . $field, $direction); + $qb->addOrderBy('submission.'.$field, $direction); } $qb->setFirstResult(($page - 1) * $itemsPerPage) - ->setMaxResults($itemsPerPage); + ->setMaxResults($itemsPerPage) + ; $paginator = new Paginator($qb); return [ iterator_to_array($paginator), - count($paginator), + \count($paginator), ]; } @@ -313,20 +319,22 @@ public function findAllSubmissionsByAssignment( ->where('submission.publicationParent = :assignmentId') ->andWhere('submission.filetype = :filetype') ->setParameter('assignmentId', $assignmentId) - ->setParameter('filetype', 'file'); + ->setParameter('filetype', 'file') + ; foreach ($order as $field => $direction) { - $qb->addOrderBy('submission.' . $field, $direction); + $qb->addOrderBy('submission.'.$field, $direction); } $qb->setFirstResult(($page - 1) * $itemsPerPage) - ->setMaxResults($itemsPerPage); + ->setMaxResults($itemsPerPage) + ; $paginator = new Paginator($qb); return [ iterator_to_array($paginator), - count($paginator), + \count($paginator), ]; } @@ -337,7 +345,8 @@ public function findUserIdsWithSubmissions(int $assignmentId): array ->join('sp.user', 'u') ->where('sp.publicationParent = :assignmentId') ->andWhere('sp.active IN (0,1)') - ->setParameter('assignmentId', $assignmentId); + ->setParameter('assignmentId', $assignmentId) + ; return array_column($qb->getQuery()->getArrayResult(), 'id'); } diff --git a/var/vue_templates/components/SidebarLogin.vue b/var/vue_templates/components/SidebarLogin.vue index 77053a50d4e..59385f6c692 100644 --- a/var/vue_templates/components/SidebarLogin.vue +++ b/var/vue_templates/components/SidebarLogin.vue @@ -1,10 +1,10 @@