Commit 419bf0fc by sawit

Permission Management จัดการสิทธิ์พนักงาน และแอป

parent 1d223776
.container {
max-width: 1400px;
}
@keyframes float {
0%, 100% {
transform: translateY(0px) scale(1);
opacity: 0.9;
}
25% {
transform: translateY(-5px) scale(1.01);
opacity: 1;
}
50% {
transform: translateY(-10px) scale(1.02);
opacity: 1;
}
75% {
transform: translateY(-5px) scale(1.01);
opacity: 1;
}
}
@keyframes pulse-glow {
0%, 100% {
box-shadow: 0 0 20px rgba(99, 102, 241, 0.3);
transform: scale(1);
opacity: 0.8;
}
25% {
box-shadow: 0 0 25px rgba(99, 102, 241, 0.4);
transform: scale(1.02);
opacity: 0.9;
}
50% {
box-shadow: 0 0 30px rgba(99, 102, 241, 0.6);
transform: scale(1.05);
opacity: 1;
}
75% {
box-shadow: 0 0 25px rgba(99, 102, 241, 0.5);
transform: scale(1.03);
opacity: 0.95;
}
}
@keyframes slideInFromLeft {
0% {
transform: translateX(-100px) scale(0.9);
opacity: 0;
}
50% {
transform: translateX(-20px) scale(0.95);
opacity: 0.5;
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes slideInFromRight {
0% {
transform: translateX(100px) scale(0.9);
opacity: 0;
}
50% {
transform: translateX(20px) scale(0.95);
opacity: 0.5;
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes fadeInScale {
0% {
transform: scale(0.8) translateY(20px);
opacity: 0;
}
50% {
transform: scale(0.95) translateY(10px);
opacity: 0.7;
}
100% {
transform: scale(1) translateY(0);
opacity: 1;
}
}
.glass-effect {
background: rgba(255, 255, 255, 0.25);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}
.group {
animation: fadeInScale 0.6s ease-out;
&:nth-child(1) { animation-delay: 0.1s; }
&:nth-child(2) { animation-delay: 0.2s; }
&:nth-child(3) { animation-delay: 0.3s; }
&:nth-child(4) { animation-delay: 0.4s; }
&:nth-child(5) { animation-delay: 0.5s; }
&:nth-child(6) { animation-delay: 0.6s; }
&:nth-child(7) { animation-delay: 0.7s; }
&:nth-child(8) { animation-delay: 0.8s; }
}
.group:hover {
animation: float 2s ease-in-out infinite;
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
button {
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
&:hover::before {
left: 100%;
}
}
.bg-white\/80 {
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
border-radius: inherit;
pointer-events: none;
}
}
.bg-gradient-to-r {
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transform: translateX(-100%);
animation: shimmer 2s infinite;
}
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
i {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.group:hover i {
transform: scale(1.1) rotate(5deg);
}
.inline-flex {
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transform: translateY(-2px) scale(1.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.space-y-4 > * {
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(to bottom, #3b82f6, #8b5cf6);
border-radius: 0 2px 2px 0;
transform: scaleY(0);
transition: transform 0.3s ease;
}
&:hover::before {
transform: scaleY(1);
}
&:hover {
transform: translateX(8px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
}
@media (max-width: 1024px) {
.container {
padding: 1.5rem;
}
.grid {
gap: 1.5rem;
}
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.grid {
gap: 1rem;
}
.text-5xl {
font-size: 2.5rem;
}
.text-6xl {
font-size: 3rem;
}
.text-3xl {
font-size: 1.875rem;
}
.text-2xl {
font-size: 1.5rem;
}
.group {
padding: 1.5rem;
}
}
@media (max-width: 640px) {
.flex.justify-between {
flex-direction: column;
align-items: flex-start;
gap: 1.5rem;
}
.text-right {
text-align: left;
}
.grid-cols-1 {
grid-template-columns: 1fr;
}
.space-y-4 > * {
padding: 1rem;
}
}
.loading {
opacity: 0.6;
pointer-events: none;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
animation: loading 1.5s infinite;
}
}
@keyframes loading {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.empty-state {
text-align: center;
padding: 4rem 2rem;
color: #6b7280;
position: relative;
&::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(99, 102, 241, 0.1) 0%, transparent 70%);
border-radius: 50%;
animation: pulse 2s infinite;
}
.text-6xl {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.5;
animation: float 3s ease-in-out infinite;
}
}
@keyframes pulse {
0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.7; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.3; }
}
.group:focus {
outline: 2px solid #3b82f6;
outline-offset: 4px;
border-radius: 1rem;
}
button:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
border-radius: 4px;
transition: background 0.3s ease;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, #2563eb, #7c3aed);
}
@media print {
.ti-btn, button {
display: none;
}
.group {
break-inside: avoid;
box-shadow: none;
border: 1px solid #000;
background: white !important;
}
.bg-gradient-to-r, .bg-gradient-to-br {
background: white !important;
color: black !important;
}
}
@media (prefers-color-scheme: dark) {
.bg-white\/80 {
background: rgba(31, 41, 55, 0.8);
color: white;
}
.text-gray-800 {
color: #f9fafb;
}
.text-gray-600 {
color: #d1d5db;
}
}
@keyframes floatUpDown {
0%, 100% {
transform: translateY(0px) rotate(0deg) scale(1);
opacity: 0.8;
}
25% {
transform: translateY(-15px) rotate(0.5deg) scale(1.02);
opacity: 0.9;
}
50% {
transform: translateY(-25px) rotate(1deg) scale(1.05);
opacity: 1;
}
75% {
transform: translateY(-10px) rotate(0.5deg) scale(1.02);
opacity: 0.9;
}
}
@keyframes drift {
0% {
transform: translateX(0px) translateY(0px) scale(1);
opacity: 0.7;
}
25% {
transform: translateX(20px) translateY(-20px) scale(1.03);
opacity: 0.8;
}
50% {
transform: translateX(35px) translateY(-35px) scale(1.05);
opacity: 0.9;
}
75% {
transform: translateX(15px) translateY(-15px) scale(1.02);
opacity: 0.8;
}
100% {
transform: translateX(0px) translateY(0px) scale(1);
opacity: 0.7;
}
}
@keyframes rotate {
0% {
transform: rotate(0deg) scale(1);
opacity: 0.6;
}
50% {
transform: rotate(180deg) scale(1.1);
opacity: 0.8;
}
100% {
transform: rotate(360deg) scale(1);
opacity: 0.6;
}
}
@keyframes shimmer {
0% {
background-position: -200% 0;
opacity: 0.5;
}
50% {
background-position: 0% 0;
opacity: 0.8;
}
100% {
background-position: 200% 0;
opacity: 0.5;
}
}
@keyframes particleFloat {
0%, 100% {
transform: translateY(0px) translateX(0px) scale(1);
opacity: 0.4;
}
25% {
transform: translateY(-8px) translateX(3px) scale(1.05);
opacity: 0.7;
}
50% {
transform: translateY(-12px) translateX(-2px) scale(1.1);
opacity: 0.9;
}
75% {
transform: translateY(-6px) translateX(4px) scale(1.03);
opacity: 0.6;
}
}
@keyframes particleGlow {
0%, 100% {
box-shadow: 0 0 5px currentColor;
transform: scale(1);
}
50% {
box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;
transform: scale(1.1);
}
}
.background-animations .absolute {
&:nth-child(1) {
animation: floatUpDown 12s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(2) {
animation: drift 16s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(3) {
animation: floatUpDown 14s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
}
.background-animations .absolute.-top-40 {
animation: floatUpDown 20s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
.background-animations .absolute.top-1\/2 {
animation: drift 24s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
.background-animations .absolute.bottom-0 {
animation: floatUpDown 22s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
.background-animations .border {
animation: rotate 40s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
&:nth-child(2) {
animation: rotate 35s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
&:nth-child(3) {
animation: rotate 45s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
}
@keyframes particleFloat {
0%, 100% {
transform: translateY(0px) translateX(0px) scale(1);
opacity: 0.4;
}
25% {
transform: translateY(-10px) translateX(5px) scale(1.1);
opacity: 0.8;
}
50% {
transform: translateY(-5px) translateX(-5px) scale(0.9);
opacity: 0.6;
}
75% {
transform: translateY(-15px) translateX(3px) scale(1.05);
opacity: 0.7;
}
}
@keyframes particleGlow {
0%, 100% {
box-shadow: 0 0 5px currentColor;
}
50% {
box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;
}
}
.absolute.inset-0 .absolute {
animation: particleFloat 8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
&:nth-child(1) {
animation: particleFloat 10s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(2) {
animation: particleFloat 9s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
&:nth-child(3) {
animation: particleFloat 11s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(4) {
animation: particleFloat 8.5s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
&:nth-child(5) {
animation: particleFloat 10.5s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:hover {
animation: particleGlow 3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity, box-shadow;
}
}
.backdrop-blur-sm {
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
.absolute {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, opacity;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.background-animations,
.background-animations .absolute,
.group,
.absolute.inset-0 .absolute {
transform: translateZ(0);
-webkit-transform: translateZ(0);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.group {
animation: fadeInScale 0.8s cubic-bezier(0.4, 0, 0.2, 1);
transform: none;
will-change: transform, opacity;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
animation: float 3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
transform: translateY(-8px) scale(1.02);
will-change: transform, opacity;
}
}
.floating-circle-2 {
animation-delay: 2s;
}
.floating-circle-3 {
animation-delay: 4s;
}
.particle-1 {
animation-delay: 0s;
}
.particle-2 {
animation-delay: 1s;
}
.particle-3 {
animation-delay: 2s;
}
.particle-4 {
animation-delay: 3s;
}
.particle-5 {
animation-delay: 4s;
}
.grid-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(99, 102, 241, 0.15) 1px, transparent 0);
background-size: 40px 40px;
}
.wave-gradient {
background: linear-gradient(to top, rgba(99, 102, 241, 0.1), transparent);
background-image: linear-gradient(to top, rgba(99, 102, 241, 0.1), transparent);
}
.main-bg-gradient {
background: linear-gradient(135deg, #e0e7ff 0%, #ffffff 50%, #ecfeff 100%);
}
.floating-circle-1 {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(147, 51, 234, 0.2) 100%);
}
.floating-circle-2 {
background: linear-gradient(135deg, rgba(244, 114, 182, 0.2) 0%, rgba(249, 115, 22, 0.2) 100%);
}
.floating-circle-3 {
background: linear-gradient(135deg, rgba(34, 197, 94, 0.2) 0%, rgba(20, 184, 166, 0.2) 100%);
}
.glassmorphism-overlay {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
}
.glass-card {
background: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
transform: translateY(-8px);
}
}
.glass-status {
background: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: 9999px;
padding: 0.5rem 1rem;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.bg-white\/80 {
background-color: rgba(255, 255, 255, 0.8);
}
.backdrop-blur-sm {
-webkit-backdrop-filter: blur(4px);
backdrop-filter: blur(4px);
}
.rounded-2xl {
border-radius: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.shadow-xl {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.border-white\/20 {
border-color: rgba(255, 255, 255, 0.2);
}
.hover\:shadow-2xl:hover {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
.transform {
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.hover\:-translate-y-2:hover {
--tw-translate-y: -0.5rem;
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.w-16 {
width: 4rem;
}
.h-16 {
height: 4rem;
}
.bg-gradient-to-br {
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
}
.from-blue-500 {
--tw-gradient-from: #3b82f6;
--tw-gradient-to: rgba(59, 131, 246, 0.997);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-blue-600 {
--tw-gradient-to: #2563eb;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.text-white {
color: #ffffff;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.group:hover .group-hover\:scale-110 {
--tw-scale-x: 1.1;
--tw-scale-y: 1.1;
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.transition-transform {
transition-property: transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
:root {
--tw-gradient-from: #3b82f6;
--tw-gradient-to: rgba(59, 130, 246, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-green-500 {
--tw-gradient-from: #10b981;
--tw-gradient-to: rgba(16, 185, 129, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-green-600 {
--tw-gradient-to: #059669;
}
.from-purple-500 {
--tw-gradient-from: #8b5cf6;
--tw-gradient-to: rgba(139, 92, 246, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-purple-600 {
--tw-gradient-to: #7c3aed;
}
.from-orange-500 {
--tw-gradient-from: #f97316;
--tw-gradient-to: rgba(249, 115, 22, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-orange-600 {
--tw-gradient-to: #ea580c;
}
.from-indigo-500 {
--tw-gradient-from: #6366f1;
--tw-gradient-to: rgba(24, 27, 214, 0.907);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-indigo-600 {
--tw-gradient-to: #4f46e5;
}
.w-12 {
width: 3rem;
}
.h-12 {
height: 3rem;
}
.w-20 {
width: 5rem;
}
.h-20 {
height: 5rem;
}
.w-24 {
width: 6rem;
}
.h-24 {
height: 6rem;
}
.w-32 {
width: 8rem;
}
.h-32 {
height: 8rem;
}
.w-40 {
width: 10rem;
}
.h-40 {
height: 10rem;
}
.w-64 {
width: 16rem;
}
.h-64 {
height: 16rem;
}
.w-80 {
width: 20rem;
}
.h-80 {
height: 20rem;
}
.w-96 {
width: 24rem;
}
.h-96 {
height: 24rem;
}
.company-logo-gradient {
background: linear-gradient(180deg, #1e40af 0%, #3b82f6 25%, #ffffff 50%, #dc2626 75%, #ef4444 100%);
}
.company-blue-circle {
background: linear-gradient(135deg, rgba(30, 64, 175, 0.3) 0%, rgba(59, 130, 246, 0.2) 100%);
}
.company-red-circle {
background: linear-gradient(135deg, rgba(220, 38, 38, 0.3) 0%, rgba(239, 68, 68, 0.2) 100%);
}
.company-blue-light {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(30, 64, 175, 0.1) 100%);
}
.company-grid-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(30, 64, 175, 0.2) 1px, transparent 0);
background-size: 40px 40px;
}
.company-wave-gradient {
background: linear-gradient(to top, rgba(30, 64, 175, 0.15), transparent);
background-image: linear-gradient(to top, rgba(30, 64, 175, 0.15), transparent);
}
.company-blue {
color: #1e40af;
}
.company-red {
color: #dc2626;
}
.company-blue-bg {
background-color: #1e40af;
}
.company-red-bg {
background-color: #dc2626;
}
.company-blue-light-bg {
background-color: #3b82f6;
}
.company-red-light-bg {
background-color: #ef4444;
}
.company-title-gradient {
background: linear-gradient(135deg, #ffffff 0%, #3b82f6 25%, #e4d2d2 75%, #ffffff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.mt-4 {
margin-top: 1rem;
}
.w-full {
width: 100%;
}
.bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops));
}
.from-red-500 {
--tw-gradient-from: #ef4444;
--tw-gradient-to: rgba(239, 68, 68, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-pink-600 {
--tw-gradient-to: #db2777;
}
.hover\:from-red-600:hover {
--tw-gradient-from: #dc2626;
--tw-gradient-to: rgba(220, 38, 38, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.hover\:to-pink-700:hover {
--tw-gradient-to: #be185d;
}
.text-white {
color: #ffffff;
}
.font-semibold {
font-weight: 600;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.rounded-xl {
border-radius: 0.75rem;
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
.transform {
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.hover\:scale-105:hover {
--tw-scale-x: 1.05;
--tw-scale-y: 1.05;
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.hover\:shadow-xl:hover {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
margin-left: 0.5rem;
}
.from-red-600 {
--tw-gradient-from: #dc2626;
--tw-gradient-to: rgba(220, 38, 38, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-pink-700 {
--tw-gradient-to: #be185d;
}
:root {
--tw-gradient-from: #ef4444;
--tw-gradient-to: rgba(239, 68, 68, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.font-medium {
font-weight: 500;
}
.font-bold {
font-weight: 700;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
.mb-8 {
margin-bottom: 2rem;
}
.mb-12 {
margin-bottom: 3rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.mr-2 {
margin-right: 0.5rem;
}
.mr-3 {
margin-right: 0.75rem;
}
.ml-1 {
margin-left: 0.25rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.ml-3 {
margin-left: 0.75rem;
}
.space-x-1 > :not([hidden]) ~ :not([hidden]) {
margin-left: 0.25rem;
}
.space-x-3 > :not([hidden]) ~ :not([hidden]) {
margin-left: 0.75rem;
}
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
margin-left: 1rem;
}
.rounded-full {
border-radius: 9999px;
}
.rounded-lg {
border-radius: 0.5rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.rounded-sm {
border-radius: 0.25rem;
}
.text-company-blue {
color: #1e40af;
}
.text-company-blue-light {
color: #3b82f6;
}
.text-company-blue-dark {
color: #1e3a8a;
}
.text-gray-800 {
color: #1e40af;
}
.text-company-red {
color: #dc2626;
}
.text-company-red-light {
color: #ef4444;
}
.text-gray-600 {
color: #3b82f6;
}
.text-gray-700 {
color: #1e40af;
}
.text-gray-900 {
color: #1e3a8a;
}
.bg-gray-50 {
background-color: #eff6ff;
}
.bg-gray-100 {
background-color: #dbeafe;
}
.bg-gray-200 {
background-color: #bfdbfe;
}
.border-gray-200 {
border-color: #bfdbfe;
}
.border-gray-300 {
border-color: #93c5fd;
}
.ri-module-line {
font-family: "remixicon" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: inline-block;
vertical-align: middle;
}
.ri-module-line::before {
content: "\f1c0";
}
[class^="ri-"],
[class*=" ri-"] {
font-family: "remixicon" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: inline-block;
vertical-align: middle;
line-height: 1;
}
.ri-module-line:not([class*="ri-"])::before {
content: "📦";
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
font-size: 1.2em;
}
<app-page-header [title]="'จัดการสิทธิ์แอปพลิเคชันบริษัท'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'จัดการสิทธิ์แอปพลิเคชันบริษัท'"></app-page-header>
<div *ngFor="let category of appCategories$ | async" class="mb-12">
<div class="flex items-center space-x-4">
<div
class="w-12 h-12 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-xl flex items-center justify-center text-white text-xl shadow-lg">
<i [class]="category.icon"></i>
</div>
<div>
<h2
class="text-3xl font-bold bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent mb-2">
{{ category.name }}
</h2>
<p class="text-gray-600">{{ category.description }}</p>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 mt-6">
<div
*ngFor="let app of category.apps; let i = index"
class="relative bg-white/80 backdrop-blur-sm rounded-2xl p-6 shadow-xl border border-white/20 hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2">
<!-- App Icon -->
<div class="relative mb-6">
<div
class="w-20 h-20 mx-auto bg-gradient-to-br from-indigo-100 to-purple-100 rounded-2xl flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300">
<img
[src]="app.icon"
alt="App Icon"
title="App Icon"
class="w-12 h-12 object-contain rounded-lg"
loading="lazy"/>
</div>
<!-- Online Indicator -->
<div
class="absolute -top-1 -right-1 w-6 h-6 bg-green-500 rounded-full border-2 border-white flex items-center justify-center">
<div class="w-2 h-2 bg-white rounded-full"></div>
</div>
</div>
<!-- App Info -->
<div class="text-center mb-4">
<h3
class="text-xl font-bold text-gray-800 mb-2 group-hover:text-indigo-600 transition-colors duration-300">
{{ app.displayName }}
</h3>
<p class="text-gray-600 text-sm leading-relaxed">
{{ app.description }}
</p>
</div>
<!-- Action Button -->
<div class="flex items-center justify-center mt-auto pt-4" (click)="navigateToApp(app)">
<div
class="inline-flex items-center px-4 py-2 bg-gradient-to-r from-indigo-500 to-purple-600 text-white text-sm font-semibold rounded-xl group-hover:from-indigo-600 group-hover:to-purple-700 transition-all duration-300 transform group-hover:scale-105 cursor-pointer">
<span>กำหนดสิทธิ์</span>
<i
class="ri-arrow-right-line ml-2 group-hover:translate-x-1 transition-transform duration-300">
</i>
</div>
</div>
</div>
</div>
</div>
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { PermissionAppmoduleComponent } from './permission-appmodule.component';
describe('PermissionAppmoduleComponent', () => {
let component: PermissionAppmoduleComponent;
let fixture: ComponentFixture<PermissionAppmoduleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PermissionAppmoduleComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PermissionAppmoduleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { Router, RouterModule } from '@angular/router';
import { SharedModule } from '../../../shared/shared.module';
import { Observable, of } from 'rxjs';
import { TokenService } from '../../../shared/services/token.service';
interface AppModule {
id: string;
name: string;
displayName: string;
description: string;
icon: string;
path: string;
isVisible: boolean;
category: 'applications' | 'services' | 'system';
permissions: {
view: boolean;
create: boolean;
edit: boolean;
delete: boolean;
export: boolean;
import: boolean;
};
}
interface AppCategory {
id: string;
name: string;
description: string;
icon: string;
apps: AppModule[];
}
@Component({
selector: 'app-permission-appmodule',
templateUrl: './permission-appmodule.component.html',
styleUrls: ['./permission-appmodule.component.css'],
standalone: true,
imports: [RouterModule, CommonModule, SharedModule]
})
export class PermissionAppmoduleComponent implements OnInit {
accessibleApps$: Observable<AppModule[]> | undefined;
appCategories$: Observable<AppCategory[]> | undefined;
constructor(
private tokenService: TokenService,
private router: Router
) { }
ngOnInit() {
this.loadAppCategories();
}
private loadAppCategories(): void {
this.appCategories$ = of(this.getAppCategories());
}
private loadAccessibleApps(): void {
// แสดงทุกเมนูก่อน โดยไม่ต้องตรวจสอบสิทธิ์
this.accessibleApps$ = of(this.getAppModules());
}
private getAppCategories(): AppCategory[] {
const apps = this.getAppModules();
return [
{
id: 'applications',
name: 'แอปพลิเคชันที่เข้าถึงได้',
description: 'แอปพลิเคชันหลักสำหรับการทำงานประจำวัน',
icon: 'ri-apps-line',
apps: apps.filter(app => app.category === 'applications')
},
];
}
private getAppModules(): AppModule[] {
return [
// แอปพลิเคชันที่เข้าถึงได้
{
id: 'myhr-plus',
name: 'myhr-plus',
displayName: 'myHR-Plus',
description: 'ระบบจัดการทรัพยากรบุคคลขั้นสูง',
icon: './assets/images/logoallHR/myhr-plus.jpg',
path: '/portal-manage/myhr-plus',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myhr-lite',
name: 'myhr-lite',
displayName: 'myHR-Lite',
description: 'ระบบจัดการทรัพยากรบุคคลพื้นฐาน',
icon: './assets/images/logoallHR/myHR-Lite-logo-new.png',
path: '/portal-manage/myhr-lite',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'zeeme',
name: 'zeeme',
displayName: 'Zeeme Plus',
description: 'ระบบจัดการเวลาและลงเวลา',
icon: './assets/images/logoallHR/zeemePlus.png',
path: '/portal-manage/zeeme',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myface',
name: 'myface',
displayName: 'myFace',
description: 'ระบบจัดการใบหน้าและความปลอดภัย',
icon: './assets/images/logoallHR/logo_myface.png',
path: '/portal-manage/myface',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'mylearn',
name: 'mylearn',
displayName: 'myLearn',
description: 'ระบบจัดการการเรียนรู้และฝึกอบรม',
icon: './assets/images/logoallHR/mylearn-logo.png',
path: '/portal-manage/mylearn',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myjob',
name: 'myjob',
displayName: 'myJob',
description: 'ระบบจัดการงานและโครงการ',
icon: './assets/images/logoallHR/logo_myjob.png',
path: '/portal-manage/myjob',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myskill-x',
name: 'myskill-x',
displayName: 'mySkill-X',
description: 'ระบบจัดการทักษะและความสามารถ',
icon: './assets/images/logoallHR/mySkill-x.png',
path: '/portal-manage/myskill-x',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'meetingBooking',
name: 'meeting-booking',
displayName: 'Meeting Booking',
description: 'ระบบจองห้องประชุม',
icon: './assets/images/logoallHR/booking.png',
path: '/portal-manage/meeting-booking',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
// การตั้งค่าระบบ
{
id: 'permissionManagement',
name: 'permission-management',
displayName: 'Permission Management',
description: 'ระบบจัดการสิทธิ์และบทบาท',
icon: './assets/images/logoallHR/permission.png',
path: '/portal-manage/permission-management',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'dashboardManagement',
name: 'dashboard-management',
displayName: 'Dashboard Management',
description: 'ระบบจัดการแดชบอร์ด และคลังวิดเจ็ต',
icon: './assets/images/logoallHR/widget.webp',
path: '/portal-manage/dashboard-management',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'my-portal',
name: 'my-portal',
displayName: 'my-Portal',
description: 'ระบบจัดการเอกสารและเมนูกลางสำหรับแอปพลิเคชันต่างๆ',
icon: './assets/images/logoallHR/portal.webp',
path: '/portal-manage/my-portal',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
}
];
}
navigateToApp(app: AppModule) {
}
onToggleAppVisibility(app: AppModule, event: Event) {
const input = event.target as HTMLInputElement;
app.isVisible = input.checked;
console.log(`Toggled ${app.name}: ${app.isVisible}`);
// Here you would typically call a service to save this change
}
}
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
</td> --> </td> -->
<td> <td>
<div class="flex flex-row items-center !gap-2 "> <div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="view(item)" data-hs-overlay="#modal-detail" <a aria-label="anchor" [routerLink]="['../permission-menumodule']"
class="ti-btn ti-btn-wave !gap-0 !m-0 bg-info/10 text-info hover:bg-info hover:text-white hover:border-info"> class="ti-btn ti-btn-wave !gap-0 !m-0 bg-info/10 text-info hover:bg-info hover:text-white hover:border-info">
<i class="ri-pencil-line"></i> <i class="ri-pencil-line"></i>
</a> </a>
......
...@@ -5,8 +5,6 @@ import { NgSelectModule } from '@ng-select/ng-select'; ...@@ -5,8 +5,6 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { SharedModule } from '../../../shared/shared.module'; import { SharedModule } from '../../../shared/shared.module';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import swal from 'sweetalert'; import swal from 'sweetalert';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
...@@ -110,55 +108,6 @@ export class PermissionEmployeelistComponent implements OnInit { ...@@ -110,55 +108,6 @@ export class PermissionEmployeelistComponent implements OnInit {
} }
} }
// uploadConfig() {
// this.uploaderProfile = new FileUploader({
// url: environment.baseUrl + "/files/upload-image",
// isHTML5: true,
// authToken: this.tokenService.getToken()!,
// });
// this.uploaderProfile.onAfterAddingFile = (fileItem: FileItem) => {
// fileItem.withCredentials = false;
// this.uploadErrorMsg = "";
// while (this.uploaderProfile!.queue.length > 1) {
// this.uploaderProfile!.queue[0].remove();
// }
// if (fileItem.file.size > 5000000) {
// this.uploadErrorMsg = "maximum file size 5mb.";
// swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
// fileItem.isCancel = true;
// return;
// }
// if (fileItem.file.type!.indexOf("image") === -1) {
// this.uploadErrorMsg = "please upload image only.";
// swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
// fileItem.isCancel = true;
// return;
// }
// fileItem.upload();
// };
// this.uploaderProfile.onCompleteItem = (
// item: FileItem,
// response: string,
// status: number,
// headers: ParsedResponseHeaders
// ) => {
// if (item.isSuccess) {
// const res = JSON.parse(response);
// console.log("res", res);
// // this.selectModel.photoGraph = res.resultObject;
// swal(res.message, "บันทึกสำเร็จ", "success");
// } else {
// this.uploadErrorMsg = "cannot upload file.";
// swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
// }
// };
// }
getEmployeeList() { getEmployeeList() {
this.permissionService.getAllEmployeesMini().subscribe( this.permissionService.getAllEmployeesMini().subscribe(
...@@ -190,12 +139,6 @@ export class PermissionEmployeelistComponent implements OnInit { ...@@ -190,12 +139,6 @@ export class PermissionEmployeelistComponent implements OnInit {
this.getEmployeeList(); this.getEmployeeList();
} }
// onEmailChange(email: string) {
// const lowerEmail = email.trim().toLowerCase();
// this.isEmailDuplicate = this.existingEmails.some(
// user => user.email && user.email.toLowerCase() === lowerEmail
// );
// }
filter(v: string) { filter(v: string) {
return this.itemsList?.filter( return this.itemsList?.filter(
...@@ -208,26 +151,6 @@ export class PermissionEmployeelistComponent implements OnInit { ...@@ -208,26 +151,6 @@ export class PermissionEmployeelistComponent implements OnInit {
); );
} }
// delete(item: PermissionModel2) {
// swal({
// title: "คุณแน่ใจหรือไม่?",
// text: "คุณจะไม่สามารถกู้คืนข้อมูลนี้ได้!",
// icon: "warning",
// dangerMode: true,
// buttons: ["ยกเลิก", "ใช่, ลบเลย!"],
// })
// .then((willDelete: any) => {
// if (willDelete) {
// this.permissionService.put(item).subscribe(result => {
// swal("ลบสำเร็จ!!", "ลบข้อมูลสำเร็จ", "success");
// this.ngOnInit();
// }, error => {
// console.error("เกิดข้อผิดพลาดในการลบ:", error);
// swal("ข้อผิดพลาด!!", "ไม่สามารถลบข้อมูลได้", "error");
// });
// }
// });
// }
new() { new() {
this.action = 'add'; this.action = 'add';
...@@ -249,94 +172,6 @@ export class PermissionEmployeelistComponent implements OnInit { ...@@ -249,94 +172,6 @@ export class PermissionEmployeelistComponent implements OnInit {
this.confirmNewPassword = ''; this.confirmNewPassword = '';
} }
view(item: PermissionModel2) {
this.action = 'edit';
// this.selectModel = new PermissionModel2(item);
this.showPasswordFields = false;
this.newPassword = '';
this.confirmNewPassword = '';
console.log(this.selectModel);
}
// save() {
// swal({
// title: "คุณแน่ใจหรือไม่?",
// text: "คุณต้องการบันทึกหรือไม่",
// icon: "warning",
// dangerMode: false,
// buttons: ["ยกเลิก", "ยืนยัน"],
// })
// .then((willSave: any) => {
// if (willSave) {
// if (this.action === 'add') {
// const body = new AuthModel();
// body.username = this.selectModel.email;
// body.password = this.password;
// body.role = new RoleModel({ roleId: 'employer' });
// const rawCompany = { ...this.selectModel };
// body.company = new PermissionModel(rawCompany);
// body.company.status = 1; // Fix status to 1 on creation
// this.userService.registerCompany(body).subscribe({
// next: res => {
// swal("Save Success!!", "บันทึกข้อมูลบริษัท", "success");
// // Optimistic UI Update
// const newCompany = new PermissionModel(this.selectModel, this.translate);
// newCompany.status = 1; // Ensure status is 1 in the UI as well
// this.itemsList.unshift(newCompany);
// this.filterList.unshift(newCompany);
// this.updatePagedItems();
// this.childModal?.nativeElement.click();
// },
// error: err => {
// console.error('Error response:', err);
// const errorMessage = err?.error?.message || err?.message || 'ไม่ทราบสาเหตุ';
// if (errorMessage.includes('email')) {
// swal("Save Failed", "อีเมลซ้ำหรือไม่ถูกต้อง", "warning");
// } else {
// swal("Error", errorMessage, "error");
// }
// }
// });
// } else if (this.action === 'edit') {
// const observables: Observable<any>[] = [];
// // Observable for updating company details
// // observables.push(this.permissionService.saveOrUpdateCompany(this.selectModel));
// // Check if password needs to be updated
// if (this.showPasswordFields && this.newPassword && this.newPassword.length >= 8 && this.newPassword === this.confirmNewPassword) {
// const authData = new AuthModel();
// authData.username = this.selectModel.email;
// authData.password = this.newPassword;
// authData.company = new PermissionModel2({ employeeId: this.selectModel.employeeId });
// // Observable for updating password
// observables.push(this.userService.editPasswordCompany(authData));
// } else if (this.showPasswordFields && (this.newPassword || this.confirmNewPassword)) {
// // If password fields are shown but validation fails, show an error and stop.
// swal("Invalid Password", "Please ensure passwords match and are at least 8 characters long.", "warning");
// return; // Stop the save process
// }
// forkJoin(observables).subscribe({
// next: results => {
// swal("Update Success!!", "บันทึกข้อมูลบริษัทเรียบร้อยแล้ว", "success");
// this.ngOnInit();
// this.childModal?.nativeElement.click();
// },
// error: err => {
// console.error('Update failed:', err);
// swal("Error", "เกิดข้อผิดพลาดในการอัปเดตข้อมูล", "error");
// }
// });
// }
// }
// });
// }
updatePagedItems() { updatePagedItems() {
this.totalCount = this.filterList.length; this.totalCount = this.filterList.length;
...@@ -364,98 +199,6 @@ export class PermissionEmployeelistComponent implements OnInit { ...@@ -364,98 +199,6 @@ export class PermissionEmployeelistComponent implements OnInit {
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.employeeId)); this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.employeeId));
} }
// deleteSelect() {
// let employerInfo = '';
// this.selectedItems.forEach((isSelected, employeeId) => {
// if (isSelected) {
// const com = this.itemsList.find(com => com.employeeId === employeeId);
// if (com) {
// employerInfo += `${this.translate.instant('Fullname')}: ${com.getName()}\n`;
// }
// }
// });
// swal({
// title: "Are you sure?",
// text: employerInfo,
// icon: "warning",
// dangerMode: true,
// buttons: ["Cancel", "Yes, Delete it!"],
// })
// .then((willDelete: any) => {
// if (willDelete) {
// this.selectedItems.forEach((isSelected, employeeId) => {
// if (isSelected) {
// const com = this.itemsList.find(com => com.employeeId === employeeId);
// if (com) {
// this.permissionService.put(com).subscribe(result => {
// swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
// this.ngOnInit();
// });
// }
// }
// });
// }
// });
// }
// adjustSelect(status: number) {
// let title = "คุณแน่ใจหรือไม่?";
// let companyInfo = '';
// const selectedCompanies: PermissionModel2[] = [];
// this.selectedItems.forEach((isSelected, employeeId) => {
// if (isSelected) {
// const company = this.itemsList.find(c => c.employeeId === employeeId);
// if (company) {
// companyInfo += `${this.translate.instant('ชื่อบริษัท')}: ${company.fname}\n`;
// selectedCompanies.push(company);
// }
// }
// });
// if (selectedCompanies.length === 0) {
// swal("ข้อผิดพลาด", "กรุณาเลือกบริษัทที่ต้องการปรับสถานะ", "warning");
// return;
// }
// swal({
// title: title,
// text: companyInfo,
// icon: "warning",
// dangerMode: false,
// buttons: ["ยกเลิก", "ยืนยัน"],
// })
// .then((willAdjust: any) => {
// if (willAdjust) {
// const updatePromises = selectedCompanies.map(company => {
// company.status = status;
// console.log(company);
// const respone = new PermissionModel2(company);
// return this.permissionService.saveOrUpdateCompany(respone).toPromise()
// .then(() => true)
// .catch(error => {
// console.error(`Error updating status for company ${company.employeeId}:`, error);
// return false;
// });
// });
// Promise.all(updatePromises)
// .then(results => {
// const allSuccessful = results.every(success => success);
// if (allSuccessful) {
// swal("บันทึกสำเร็จ!!", "บันทึกข้อมูลสำเร็จ", "success");
// } else {
// swal("สำเร็จบางส่วน/ข้อผิดพลาด!!", "มีการอัปเดตสถานะบางส่วนไม่สำเร็จ หรือมีข้อผิดพลาด", "warning");
// }
// this.ngOnInit();
// this.selectedItems.clear();
// this.allSelected = false;
// this.someSelected = false;
// });
// }
// });
// }
triggerFileInput(): void { triggerFileInput(): void {
if (this.profileChangeInputRef) { if (this.profileChangeInputRef) {
this.profileChangeInputRef.nativeElement.click(); this.profileChangeInputRef.nativeElement.click();
......
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { PermissionMenulistComponent } from './permission-menulist.component';
describe('PermissionMenulistComponent', () => {
let component: PermissionMenulistComponent;
let fixture: ComponentFixture<PermissionMenulistComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PermissionMenulistComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PermissionMenulistComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SharedModule } from '../../../../shared/shared.module';
@Component({
selector: 'app-permission-menulist',
templateUrl: './permission-menulist.component.html',
styleUrls: ['./permission-menulist.component.css'],
standalone: true,
imports: [RouterModule, CommonModule, SharedModule]
})
export class PermissionMenulistComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
.container {
max-width: 1400px;
}
@keyframes float {
0%, 100% {
transform: translateY(0px) scale(1);
opacity: 0.9;
}
25% {
transform: translateY(-5px) scale(1.01);
opacity: 1;
}
50% {
transform: translateY(-10px) scale(1.02);
opacity: 1;
}
75% {
transform: translateY(-5px) scale(1.01);
opacity: 1;
}
}
@keyframes pulse-glow {
0%, 100% {
box-shadow: 0 0 20px rgba(99, 102, 241, 0.3);
transform: scale(1);
opacity: 0.8;
}
25% {
box-shadow: 0 0 25px rgba(99, 102, 241, 0.4);
transform: scale(1.02);
opacity: 0.9;
}
50% {
box-shadow: 0 0 30px rgba(99, 102, 241, 0.6);
transform: scale(1.05);
opacity: 1;
}
75% {
box-shadow: 0 0 25px rgba(99, 102, 241, 0.5);
transform: scale(1.03);
opacity: 0.95;
}
}
@keyframes slideInFromLeft {
0% {
transform: translateX(-100px) scale(0.9);
opacity: 0;
}
50% {
transform: translateX(-20px) scale(0.95);
opacity: 0.5;
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes slideInFromRight {
0% {
transform: translateX(100px) scale(0.9);
opacity: 0;
}
50% {
transform: translateX(20px) scale(0.95);
opacity: 0.5;
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes fadeInScale {
0% {
transform: scale(0.8) translateY(20px);
opacity: 0;
}
50% {
transform: scale(0.95) translateY(10px);
opacity: 0.7;
}
100% {
transform: scale(1) translateY(0);
opacity: 1;
}
}
.glass-effect {
background: rgba(255, 255, 255, 0.25);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}
.group {
animation: fadeInScale 0.6s ease-out;
&:nth-child(1) { animation-delay: 0.1s; }
&:nth-child(2) { animation-delay: 0.2s; }
&:nth-child(3) { animation-delay: 0.3s; }
&:nth-child(4) { animation-delay: 0.4s; }
&:nth-child(5) { animation-delay: 0.5s; }
&:nth-child(6) { animation-delay: 0.6s; }
&:nth-child(7) { animation-delay: 0.7s; }
&:nth-child(8) { animation-delay: 0.8s; }
}
.group:hover {
animation: float 2s ease-in-out infinite;
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
button {
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
&:hover::before {
left: 100%;
}
}
.bg-white\/80 {
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
border-radius: inherit;
pointer-events: none;
}
}
.bg-gradient-to-r {
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transform: translateX(-100%);
animation: shimmer 2s infinite;
}
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
i {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.group:hover i {
transform: scale(1.1) rotate(5deg);
}
.inline-flex {
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transform: translateY(-2px) scale(1.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.space-y-4 > * {
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(to bottom, #3b82f6, #8b5cf6);
border-radius: 0 2px 2px 0;
transform: scaleY(0);
transition: transform 0.3s ease;
}
&:hover::before {
transform: scaleY(1);
}
&:hover {
transform: translateX(8px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
}
@media (max-width: 1024px) {
.container {
padding: 1.5rem;
}
.grid {
gap: 1.5rem;
}
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.grid {
gap: 1rem;
}
.text-5xl {
font-size: 2.5rem;
}
.text-6xl {
font-size: 3rem;
}
.text-3xl {
font-size: 1.875rem;
}
.text-2xl {
font-size: 1.5rem;
}
.group {
padding: 1.5rem;
}
}
@media (max-width: 640px) {
.flex.justify-between {
flex-direction: column;
align-items: flex-start;
gap: 1.5rem;
}
.text-right {
text-align: left;
}
.grid-cols-1 {
grid-template-columns: 1fr;
}
.space-y-4 > * {
padding: 1rem;
}
}
.loading {
opacity: 0.6;
pointer-events: none;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
animation: loading 1.5s infinite;
}
}
@keyframes loading {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.empty-state {
text-align: center;
padding: 4rem 2rem;
color: #6b7280;
position: relative;
&::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(99, 102, 241, 0.1) 0%, transparent 70%);
border-radius: 50%;
animation: pulse 2s infinite;
}
.text-6xl {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.5;
animation: float 3s ease-in-out infinite;
}
}
@keyframes pulse {
0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.7; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.3; }
}
.group:focus {
outline: 2px solid #3b82f6;
outline-offset: 4px;
border-radius: 1rem;
}
button:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
border-radius: 4px;
transition: background 0.3s ease;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, #2563eb, #7c3aed);
}
@media print {
.ti-btn, button {
display: none;
}
.group {
break-inside: avoid;
box-shadow: none;
border: 1px solid #000;
background: white !important;
}
.bg-gradient-to-r, .bg-gradient-to-br {
background: white !important;
color: black !important;
}
}
@media (prefers-color-scheme: dark) {
.bg-white\/80 {
background: rgba(31, 41, 55, 0.8);
color: white;
}
.text-gray-800 {
color: #f9fafb;
}
.text-gray-600 {
color: #d1d5db;
}
}
@keyframes floatUpDown {
0%, 100% {
transform: translateY(0px) rotate(0deg) scale(1);
opacity: 0.8;
}
25% {
transform: translateY(-15px) rotate(0.5deg) scale(1.02);
opacity: 0.9;
}
50% {
transform: translateY(-25px) rotate(1deg) scale(1.05);
opacity: 1;
}
75% {
transform: translateY(-10px) rotate(0.5deg) scale(1.02);
opacity: 0.9;
}
}
@keyframes drift {
0% {
transform: translateX(0px) translateY(0px) scale(1);
opacity: 0.7;
}
25% {
transform: translateX(20px) translateY(-20px) scale(1.03);
opacity: 0.8;
}
50% {
transform: translateX(35px) translateY(-35px) scale(1.05);
opacity: 0.9;
}
75% {
transform: translateX(15px) translateY(-15px) scale(1.02);
opacity: 0.8;
}
100% {
transform: translateX(0px) translateY(0px) scale(1);
opacity: 0.7;
}
}
@keyframes rotate {
0% {
transform: rotate(0deg) scale(1);
opacity: 0.6;
}
50% {
transform: rotate(180deg) scale(1.1);
opacity: 0.8;
}
100% {
transform: rotate(360deg) scale(1);
opacity: 0.6;
}
}
@keyframes shimmer {
0% {
background-position: -200% 0;
opacity: 0.5;
}
50% {
background-position: 0% 0;
opacity: 0.8;
}
100% {
background-position: 200% 0;
opacity: 0.5;
}
}
@keyframes particleFloat {
0%, 100% {
transform: translateY(0px) translateX(0px) scale(1);
opacity: 0.4;
}
25% {
transform: translateY(-8px) translateX(3px) scale(1.05);
opacity: 0.7;
}
50% {
transform: translateY(-12px) translateX(-2px) scale(1.1);
opacity: 0.9;
}
75% {
transform: translateY(-6px) translateX(4px) scale(1.03);
opacity: 0.6;
}
}
@keyframes particleGlow {
0%, 100% {
box-shadow: 0 0 5px currentColor;
transform: scale(1);
}
50% {
box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;
transform: scale(1.1);
}
}
.background-animations .absolute {
&:nth-child(1) {
animation: floatUpDown 12s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(2) {
animation: drift 16s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(3) {
animation: floatUpDown 14s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
}
.background-animations .absolute.-top-40 {
animation: floatUpDown 20s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
.background-animations .absolute.top-1\/2 {
animation: drift 24s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
.background-animations .absolute.bottom-0 {
animation: floatUpDown 22s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
.background-animations .border {
animation: rotate 40s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
&:nth-child(2) {
animation: rotate 35s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
&:nth-child(3) {
animation: rotate 45s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
}
@keyframes particleFloat {
0%, 100% {
transform: translateY(0px) translateX(0px) scale(1);
opacity: 0.4;
}
25% {
transform: translateY(-10px) translateX(5px) scale(1.1);
opacity: 0.8;
}
50% {
transform: translateY(-5px) translateX(-5px) scale(0.9);
opacity: 0.6;
}
75% {
transform: translateY(-15px) translateX(3px) scale(1.05);
opacity: 0.7;
}
}
@keyframes particleGlow {
0%, 100% {
box-shadow: 0 0 5px currentColor;
}
50% {
box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;
}
}
.absolute.inset-0 .absolute {
animation: particleFloat 8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
&:nth-child(1) {
animation: particleFloat 10s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(2) {
animation: particleFloat 9s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
&:nth-child(3) {
animation: particleFloat 11s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:nth-child(4) {
animation: particleFloat 8.5s cubic-bezier(0.4, 0, 0.2, 1) infinite reverse;
will-change: transform, opacity;
}
&:nth-child(5) {
animation: particleFloat 10.5s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity;
}
&:hover {
animation: particleGlow 3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform, opacity, box-shadow;
}
}
.backdrop-blur-sm {
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
.absolute {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, opacity;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.background-animations,
.background-animations .absolute,
.group,
.absolute.inset-0 .absolute {
transform: translateZ(0);
-webkit-transform: translateZ(0);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.group {
animation: fadeInScale 0.8s cubic-bezier(0.4, 0, 0.2, 1);
transform: none;
will-change: transform, opacity;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
animation: float 3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
transform: translateY(-8px) scale(1.02);
will-change: transform, opacity;
}
}
.floating-circle-2 {
animation-delay: 2s;
}
.floating-circle-3 {
animation-delay: 4s;
}
.particle-1 {
animation-delay: 0s;
}
.particle-2 {
animation-delay: 1s;
}
.particle-3 {
animation-delay: 2s;
}
.particle-4 {
animation-delay: 3s;
}
.particle-5 {
animation-delay: 4s;
}
.grid-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(99, 102, 241, 0.15) 1px, transparent 0);
background-size: 40px 40px;
}
.wave-gradient {
background: linear-gradient(to top, rgba(99, 102, 241, 0.1), transparent);
background-image: linear-gradient(to top, rgba(99, 102, 241, 0.1), transparent);
}
.main-bg-gradient {
background: linear-gradient(135deg, #e0e7ff 0%, #ffffff 50%, #ecfeff 100%);
}
.floating-circle-1 {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(147, 51, 234, 0.2) 100%);
}
.floating-circle-2 {
background: linear-gradient(135deg, rgba(244, 114, 182, 0.2) 0%, rgba(249, 115, 22, 0.2) 100%);
}
.floating-circle-3 {
background: linear-gradient(135deg, rgba(34, 197, 94, 0.2) 0%, rgba(20, 184, 166, 0.2) 100%);
}
.glassmorphism-overlay {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
}
.glass-card {
background: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
transform: translateY(-8px);
}
}
.glass-status {
background: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: 9999px;
padding: 0.5rem 1rem;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.bg-white\/80 {
background-color: rgba(255, 255, 255, 0.8);
}
.backdrop-blur-sm {
-webkit-backdrop-filter: blur(4px);
backdrop-filter: blur(4px);
}
.rounded-2xl {
border-radius: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.shadow-xl {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.border-white\/20 {
border-color: rgba(255, 255, 255, 0.2);
}
.hover\:shadow-2xl:hover {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
.transform {
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.hover\:-translate-y-2:hover {
--tw-translate-y: -0.5rem;
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.w-16 {
width: 4rem;
}
.h-16 {
height: 4rem;
}
.bg-gradient-to-br {
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
}
.from-blue-500 {
--tw-gradient-from: #3b82f6;
--tw-gradient-to: rgba(59, 131, 246, 0.997);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-blue-600 {
--tw-gradient-to: #2563eb;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.text-white {
color: #ffffff;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.group:hover .group-hover\:scale-110 {
--tw-scale-x: 1.1;
--tw-scale-y: 1.1;
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.transition-transform {
transition-property: transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
:root {
--tw-gradient-from: #3b82f6;
--tw-gradient-to: rgba(59, 130, 246, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-green-500 {
--tw-gradient-from: #10b981;
--tw-gradient-to: rgba(16, 185, 129, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-green-600 {
--tw-gradient-to: #059669;
}
.from-purple-500 {
--tw-gradient-from: #8b5cf6;
--tw-gradient-to: rgba(139, 92, 246, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-purple-600 {
--tw-gradient-to: #7c3aed;
}
.from-orange-500 {
--tw-gradient-from: #f97316;
--tw-gradient-to: rgba(249, 115, 22, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-orange-600 {
--tw-gradient-to: #ea580c;
}
.from-indigo-500 {
--tw-gradient-from: #6366f1;
--tw-gradient-to: rgba(24, 27, 214, 0.907);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-indigo-600 {
--tw-gradient-to: #4f46e5;
}
.w-12 {
width: 3rem;
}
.h-12 {
height: 3rem;
}
.w-20 {
width: 5rem;
}
.h-20 {
height: 5rem;
}
.w-24 {
width: 6rem;
}
.h-24 {
height: 6rem;
}
.w-32 {
width: 8rem;
}
.h-32 {
height: 8rem;
}
.w-40 {
width: 10rem;
}
.h-40 {
height: 10rem;
}
.w-64 {
width: 16rem;
}
.h-64 {
height: 16rem;
}
.w-80 {
width: 20rem;
}
.h-80 {
height: 20rem;
}
.w-96 {
width: 24rem;
}
.h-96 {
height: 24rem;
}
.company-logo-gradient {
background: linear-gradient(180deg, #1e40af 0%, #3b82f6 25%, #ffffff 50%, #dc2626 75%, #ef4444 100%);
}
.company-blue-circle {
background: linear-gradient(135deg, rgba(30, 64, 175, 0.3) 0%, rgba(59, 130, 246, 0.2) 100%);
}
.company-red-circle {
background: linear-gradient(135deg, rgba(220, 38, 38, 0.3) 0%, rgba(239, 68, 68, 0.2) 100%);
}
.company-blue-light {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(30, 64, 175, 0.1) 100%);
}
.company-grid-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(30, 64, 175, 0.2) 1px, transparent 0);
background-size: 40px 40px;
}
.company-wave-gradient {
background: linear-gradient(to top, rgba(30, 64, 175, 0.15), transparent);
background-image: linear-gradient(to top, rgba(30, 64, 175, 0.15), transparent);
}
.company-blue {
color: #1e40af;
}
.company-red {
color: #dc2626;
}
.company-blue-bg {
background-color: #1e40af;
}
.company-red-bg {
background-color: #dc2626;
}
.company-blue-light-bg {
background-color: #3b82f6;
}
.company-red-light-bg {
background-color: #ef4444;
}
.company-title-gradient {
background: linear-gradient(135deg, #ffffff 0%, #3b82f6 25%, #e4d2d2 75%, #ffffff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.mt-4 {
margin-top: 1rem;
}
.w-full {
width: 100%;
}
.bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops));
}
.from-red-500 {
--tw-gradient-from: #ef4444;
--tw-gradient-to: rgba(239, 68, 68, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-pink-600 {
--tw-gradient-to: #db2777;
}
.hover\:from-red-600:hover {
--tw-gradient-from: #dc2626;
--tw-gradient-to: rgba(220, 38, 38, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.hover\:to-pink-700:hover {
--tw-gradient-to: #be185d;
}
.text-white {
color: #ffffff;
}
.font-semibold {
font-weight: 600;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.rounded-xl {
border-radius: 0.75rem;
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
.transform {
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.hover\:scale-105:hover {
--tw-scale-x: 1.05;
--tw-scale-y: 1.05;
transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1));
}
.shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.hover\:shadow-xl:hover {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
margin-left: 0.5rem;
}
.from-red-600 {
--tw-gradient-from: #dc2626;
--tw-gradient-to: rgba(220, 38, 38, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.to-pink-700 {
--tw-gradient-to: #be185d;
}
:root {
--tw-gradient-from: #ef4444;
--tw-gradient-to: rgba(239, 68, 68, 0);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.font-medium {
font-weight: 500;
}
.font-bold {
font-weight: 700;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
.mb-8 {
margin-bottom: 2rem;
}
.mb-12 {
margin-bottom: 3rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.mr-2 {
margin-right: 0.5rem;
}
.mr-3 {
margin-right: 0.75rem;
}
.ml-1 {
margin-left: 0.25rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.ml-3 {
margin-left: 0.75rem;
}
.space-x-1 > :not([hidden]) ~ :not([hidden]) {
margin-left: 0.25rem;
}
.space-x-3 > :not([hidden]) ~ :not([hidden]) {
margin-left: 0.75rem;
}
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
margin-left: 1rem;
}
.rounded-full {
border-radius: 9999px;
}
.rounded-lg {
border-radius: 0.5rem;
}
.rounded-md {
border-radius: 0.375rem;
}
.rounded-sm {
border-radius: 0.25rem;
}
.text-company-blue {
color: #1e40af;
}
.text-company-blue-light {
color: #3b82f6;
}
.text-company-blue-dark {
color: #1e3a8a;
}
.text-gray-800 {
color: #1e40af;
}
.text-company-red {
color: #dc2626;
}
.text-company-red-light {
color: #ef4444;
}
.text-gray-600 {
color: #3b82f6;
}
.text-gray-700 {
color: #1e40af;
}
.text-gray-900 {
color: #1e3a8a;
}
.bg-gray-50 {
background-color: #eff6ff;
}
.bg-gray-100 {
background-color: #dbeafe;
}
.bg-gray-200 {
background-color: #bfdbfe;
}
.border-gray-200 {
border-color: #bfdbfe;
}
.border-gray-300 {
border-color: #93c5fd;
}
.ri-module-line {
font-family: "remixicon" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: inline-block;
vertical-align: middle;
}
.ri-module-line::before {
content: "\f1c0";
}
[class^="ri-"],
[class*=" ri-"] {
font-family: "remixicon" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: inline-block;
vertical-align: middle;
line-height: 1;
}
.ri-module-line:not([class*="ri-"])::before {
content: "📦";
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
font-size: 1.2em;
}
<app-page-header [title]="'เลือกเมนู'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'จัดการสิทธิ์พนักงาน'" [title1Link]="'../permission-employeelist'" [title2]="'แอพลิเคชันที่เข้าถึงได้'"></app-page-header>
<div *ngFor="let category of appCategories$ | async" class="mb-12">
<div class="flex items-center space-x-4">
<div
class="w-12 h-12 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-xl flex items-center justify-center text-white text-xl shadow-lg"
>
<i [class]="category.icon"></i>
</div>
<div>
<h2
class="text-3xl font-bold bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent mb-2"
>
{{ category.name }}
</h2>
<p class="text-gray-600">{{ category.description }}</p>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 mt-6">
<div
*ngFor="let app of category.apps; let i = index"
class="relative bg-white/80 backdrop-blur-sm rounded-2xl p-6 shadow-xl border border-white/20 hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2"
>
<!-- Toggle Switch -->
<div class="absolute top-4 right-4 z-10" (click)="$event.stopPropagation()">
<label class="inline-flex items-center cursor-pointer">
<input type="checkbox" [checked]="app.isVisible" (change)="onToggleAppVisibility(app, $event)" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-300 rounded-full peer peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600"
[ngStyle]="{ 'background-color': app.isVisible ? '#3b82f6' : '#d1d5db' }"></div>
</label>
</div>
<!-- App Icon -->
<div class="relative mb-6">
<div
class="w-20 h-20 mx-auto bg-gradient-to-br from-indigo-100 to-purple-100 rounded-2xl flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform duration-300"
>
<img
[src]="app.icon"
alt="App Icon"
title="App Icon"
class="w-12 h-12 object-contain rounded-lg"
loading="lazy"
/>
</div>
<!-- Online Indicator -->
<div
class="absolute -top-1 -right-1 w-6 h-6 bg-green-500 rounded-full border-2 border-white flex items-center justify-center"
>
<div class="w-2 h-2 bg-white rounded-full"></div>
</div>
</div>
<!-- App Info -->
<div class="text-center mb-4">
<h3
class="text-xl font-bold text-gray-800 mb-2 group-hover:text-indigo-600 transition-colors duration-300"
>
{{ app.displayName }}
</h3>
<p class="text-gray-600 text-sm leading-relaxed">
{{ app.description }}
</p>
</div>
<!-- Action Button -->
<div class="flex items-center justify-center mt-auto pt-4" (click)="navigateToApp(app)">
<div
class="inline-flex items-center px-4 py-2 bg-gradient-to-r from-indigo-500 to-purple-600 text-white text-sm font-semibold rounded-xl group-hover:from-indigo-600 group-hover:to-purple-700 transition-all duration-300 transform group-hover:scale-105 cursor-pointer">
<span>กำหนดสิทธิ์</span>
<i
class="ri-arrow-right-line ml-2 group-hover:translate-x-1 transition-transform duration-300">
</i>
</div>
</div>
</div>
</div>
</div>
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { PermissionMenumoduleComponent } from './permission-menumodule.component';
describe('PermissionMenumoduleComponent', () => {
let component: PermissionMenumoduleComponent;
let fixture: ComponentFixture<PermissionMenumoduleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PermissionMenumoduleComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PermissionMenumoduleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { Router, RouterLink, RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Observable, of } from 'rxjs';
import { SharedModule } from '../../../../shared/shared.module';
import { TokenService } from '../../../../shared/services/token.service';
interface AppModule {
id: string;
name: string;
displayName: string;
description: string;
icon: string;
path: string;
isVisible: boolean;
category: 'applications' | 'services' | 'system';
permissions: {
view: boolean;
create: boolean;
edit: boolean;
delete: boolean;
export: boolean;
import: boolean;
};
}
interface AppCategory {
id: string;
name: string;
description: string;
icon: string;
apps: AppModule[];
}
@Component({
selector: 'app-permission-menumodule',
templateUrl: './permission-menumodule.component.html',
styleUrls: ['./permission-menumodule.component.css'],
standalone: true,
imports: [RouterModule, CommonModule, SharedModule]
})
export class PermissionMenumoduleComponent implements OnInit {
accessibleApps$: Observable<AppModule[]> | undefined;
appCategories$: Observable<AppCategory[]> | undefined;
constructor(
private tokenService: TokenService,
private router: Router
) { }
ngOnInit() {
this.loadAppCategories();
}
private loadAppCategories(): void {
this.appCategories$ = of(this.getAppCategories());
}
private loadAccessibleApps(): void {
// แสดงทุกเมนูก่อน โดยไม่ต้องตรวจสอบสิทธิ์
this.accessibleApps$ = of(this.getAppModules());
}
private getAppCategories(): AppCategory[] {
const apps = this.getAppModules();
return [
{
id: 'applications',
name: 'แอปพลิเคชันที่เข้าถึงได้',
description: 'แอปพลิเคชันหลักสำหรับการทำงานประจำวัน',
icon: 'ri-apps-line',
apps: apps.filter(app => app.category === 'applications')
},
];
}
private getAppModules(): AppModule[] {
return [
// แอปพลิเคชันที่เข้าถึงได้
{
id: 'myhr-plus',
name: 'myhr-plus',
displayName: 'myHR-Plus',
description: 'ระบบจัดการทรัพยากรบุคคลขั้นสูง',
icon: './assets/images/logoallHR/myhr-plus.jpg',
path: '/portal-manage/myhr-plus',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myhr-lite',
name: 'myhr-lite',
displayName: 'myHR-Lite',
description: 'ระบบจัดการทรัพยากรบุคคลพื้นฐาน',
icon: './assets/images/logoallHR/myHR-Lite-logo-new.png',
path: '/portal-manage/myhr-lite',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'zeeme',
name: 'zeeme',
displayName: 'Zeeme Plus',
description: 'ระบบจัดการเวลาและลงเวลา',
icon: './assets/images/logoallHR/zeemePlus.png',
path: '/portal-manage/zeeme',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myface',
name: 'myface',
displayName: 'myFace',
description: 'ระบบจัดการใบหน้าและความปลอดภัย',
icon: './assets/images/logoallHR/logo_myface.png',
path: '/portal-manage/myface',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'mylearn',
name: 'mylearn',
displayName: 'myLearn',
description: 'ระบบจัดการการเรียนรู้และฝึกอบรม',
icon: './assets/images/logoallHR/mylearn-logo.png',
path: '/portal-manage/mylearn',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myjob',
name: 'myjob',
displayName: 'myJob',
description: 'ระบบจัดการงานและโครงการ',
icon: './assets/images/logoallHR/logo_myjob.png',
path: '/portal-manage/myjob',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'myskill-x',
name: 'myskill-x',
displayName: 'mySkill-X',
description: 'ระบบจัดการทักษะและความสามารถ',
icon: './assets/images/logoallHR/mySkill-x.png',
path: '/portal-manage/myskill-x',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'meetingBooking',
name: 'meeting-booking',
displayName: 'Meeting Booking',
description: 'ระบบจองห้องประชุม',
icon: './assets/images/logoallHR/booking.png',
path: '/portal-manage/meeting-booking',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
// การตั้งค่าระบบ
{
id: 'permissionManagement',
name: 'permission-management',
displayName: 'Permission Management',
description: 'ระบบจัดการสิทธิ์และบทบาท',
icon: './assets/images/logoallHR/permission.png',
path: '/portal-manage/permission-management',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'dashboardManagement',
name: 'dashboard-management',
displayName: 'Dashboard Management',
description: 'ระบบจัดการแดชบอร์ด และคลังวิดเจ็ต',
icon: './assets/images/logoallHR/widget.webp',
path: '/portal-manage/dashboard-management',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'my-portal',
name: 'my-portal',
displayName: 'my-Portal',
description: 'ระบบจัดการเอกสารและเมนูกลางสำหรับแอปพลิเคชันต่างๆ',
icon: './assets/images/logoallHR/portal.webp',
path: '/portal-manage/my-portal',
isVisible: true,
category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
}
];
}
navigateToApp(app: AppModule) {
}
onToggleAppVisibility(app: AppModule, event: Event) {
const input = event.target as HTMLInputElement;
app.isVisible = input.checked;
console.log(`Toggled ${app.name}: ${app.isVisible}`);
// Here you would typically call a service to save this change
}
}
...@@ -13,6 +13,21 @@ export const PERMISSION_ROUTES: Routes = [ ...@@ -13,6 +13,21 @@ export const PERMISSION_ROUTES: Routes = [
loadComponent: () => loadComponent: () =>
import('./permission-employeelist/permission-employeelist.component').then((m) => m.PermissionEmployeelistComponent), import('./permission-employeelist/permission-employeelist.component').then((m) => m.PermissionEmployeelistComponent),
}, },
{
path: 'permission-menumodule',
loadComponent: () =>
import('./permission-employeelist/permission-menumodule/permission-menumodule.component').then((m) => m.PermissionMenumoduleComponent),
},
{
path: 'permission-menulist',
loadComponent: () =>
import('./permission-employeelist/permission-menulist/permission-menulist.component').then((m) => m.PermissionMenulistComponent),
},
{
path: 'permission-appmodule',
loadComponent: () =>
import('./permission-appmodule/permission-appmodule.component').then((m) => m.PermissionAppmoduleComponent),
},
] ]
} }
]; ];
...@@ -9,8 +9,13 @@ ...@@ -9,8 +9,13 @@
<i class="ti ti-chevrons-right flex-shrink-0 text-[#8c9097] dark:text-white/50 px-[0.5rem] overflow-visible rtl:rotate-180"></i> <i class="ti ti-chevrons-right flex-shrink-0 text-[#8c9097] dark:text-white/50 px-[0.5rem] overflow-visible rtl:rotate-180"></i>
</a> </a>
</li> </li>
<li class="text-[0.813rem] text-primary font-semibold hover:text-primary dark:text-[#8c9097] dark:text-white/50 " aria-current="page"> <li class="text-[0.813rem] text-primary hover:text-primary dark:text-[#8c9097] dark:text-white/50">
{{title1 | translate}} <a *ngIf="title1Link" [routerLink]="title1Link" class="flex items-center">{{title1 | translate}}</a>
<span *ngIf="!title1Link">{{title1 | translate}}</span>
</li>
<li class="text-[0.813rem] text-primary font-semibold hover:text-primary dark:text-[#8c9097] dark:text-white/50 " aria-current="page" *ngIf="title2">
<i class="ti ti-chevrons-right flex-shrink-0 text-[#8c9097] dark:text-white/50 px-[0.5rem] overflow-visible rtl:rotate-180"></i>
{{title2 | translate}}
</li> </li>
</ol> </ol>
</div> </div>
...@@ -8,5 +8,7 @@ import { Component, Input } from '@angular/core'; ...@@ -8,5 +8,7 @@ import { Component, Input } from '@angular/core';
export class PageHeaderComponent { export class PageHeaderComponent {
@Input() title!: string; @Input() title!: string;
@Input() title1!:string; @Input() title1!:string;
@Input() title2!:string;
@Input() activeTitle!:string; @Input() activeTitle!:string;
@Input() title1Link!: string;
} }
...@@ -470,7 +470,7 @@ export class NavService implements OnDestroy { ...@@ -470,7 +470,7 @@ export class NavService implements OnDestroy {
active: false, active: false,
children: [ children: [
{ path: '/portal-manage/permission-management/permission-employeelist', title: 'จัดการสิทธิ์และบทบาท', type: 'link' }, { path: '/portal-manage/permission-management/permission-employeelist', title: 'จัดการสิทธิ์และบทบาท', type: 'link' },
// { path: '/portal-manage/menu-permission-management', title: 'จัดการสิทธิ์เมนู', type: 'link' }, { path: '/portal-manage/permission-management/permission-appmodule', title: 'จัดการสิทธิ์แอปพลิเคชันบริษัท', type: 'link' },
// { path: '/portal-manage/user-role-management', title: 'จัดการผู้ใช้และบทบาท', type: 'link' }, // { path: '/portal-manage/user-role-management', title: 'จัดการผู้ใช้และบทบาท', type: 'link' },
], ],
}, },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment