mirror of
https://github.com/twinpath/app.git
synced 2026-01-26 05:15:28 +07:00
Initial commit
This commit is contained in:
876
resources/css/app.css
Normal file
876
resources/css/app.css
Normal file
@@ -0,0 +1,876 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap') layer(base);
|
||||
@import 'prismjs/themes/prism.min.css';
|
||||
@import 'tailwindcss';
|
||||
|
||||
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme {
|
||||
--font-*: initial;
|
||||
--font-outfit: Outfit, sans-serif;
|
||||
|
||||
--breakpoint-*: initial;
|
||||
--breakpoint-2xsm: 375px;
|
||||
--breakpoint-xsm: 425px;
|
||||
--breakpoint-3xl: 2000px;
|
||||
--breakpoint-sm: 640px;
|
||||
--breakpoint-md: 768px;
|
||||
--breakpoint-lg: 1024px;
|
||||
--breakpoint-xl: 1280px;
|
||||
--breakpoint-2xl: 1536px;
|
||||
|
||||
--text-title-2xl: 72px;
|
||||
--text-title-2xl--line-height: 90px;
|
||||
--text-title-xl: 60px;
|
||||
--text-title-xl--line-height: 72px;
|
||||
--text-title-lg: 48px;
|
||||
--text-title-lg--line-height: 60px;
|
||||
--text-title-md: 36px;
|
||||
--text-title-md--line-height: 44px;
|
||||
--text-title-sm: 30px;
|
||||
--text-title-sm--line-height: 38px;
|
||||
--text-theme-xl: 20px;
|
||||
--text-theme-xl--line-height: 30px;
|
||||
--text-theme-sm: 14px;
|
||||
--text-theme-sm--line-height: 20px;
|
||||
--text-theme-xs: 12px;
|
||||
--text-theme-xs--line-height: 18px;
|
||||
|
||||
--color-current: currentColor;
|
||||
--color-transparent: transparent;
|
||||
--color-white: #ffffff;
|
||||
--color-black: #101828;
|
||||
|
||||
--color-brand-25: #f2f7ff;
|
||||
--color-brand-50: #ecf3ff;
|
||||
--color-brand-100: #dde9ff;
|
||||
--color-brand-200: #c2d6ff;
|
||||
--color-brand-300: #9cb9ff;
|
||||
--color-brand-400: #7592ff;
|
||||
--color-brand-500: #465fff;
|
||||
--color-brand-600: #3641f5;
|
||||
--color-brand-700: #2a31d8;
|
||||
--color-brand-800: #252dae;
|
||||
--color-brand-900: #262e89;
|
||||
--color-brand-950: #161950;
|
||||
|
||||
--color-blue-light-25: #f5fbff;
|
||||
--color-blue-light-50: #f0f9ff;
|
||||
--color-blue-light-100: #e0f2fe;
|
||||
--color-blue-light-200: #b9e6fe;
|
||||
--color-blue-light-300: #7cd4fd;
|
||||
--color-blue-light-400: #36bffa;
|
||||
--color-blue-light-500: #0ba5ec;
|
||||
--color-blue-light-600: #0086c9;
|
||||
--color-blue-light-700: #026aa2;
|
||||
--color-blue-light-800: #065986;
|
||||
--color-blue-light-900: #0b4a6f;
|
||||
--color-blue-light-950: #062c41;
|
||||
|
||||
--color-gray-25: #fcfcfd;
|
||||
--color-gray-50: #f9fafb;
|
||||
--color-gray-100: #f2f4f7;
|
||||
--color-gray-200: #e4e7ec;
|
||||
--color-gray-300: #d0d5dd;
|
||||
--color-gray-400: #98a2b3;
|
||||
--color-gray-500: #667085;
|
||||
--color-gray-600: #475467;
|
||||
--color-gray-700: #344054;
|
||||
--color-gray-800: #1d2939;
|
||||
--color-gray-900: #101828;
|
||||
--color-gray-950: #0c111d;
|
||||
--color-gray-dark: #1a2231;
|
||||
|
||||
--color-orange-25: #fffaf5;
|
||||
--color-orange-50: #fff6ed;
|
||||
--color-orange-100: #ffead5;
|
||||
--color-orange-200: #fddcab;
|
||||
--color-orange-300: #feb273;
|
||||
--color-orange-400: #fd853a;
|
||||
--color-orange-500: #fb6514;
|
||||
--color-orange-600: #ec4a0a;
|
||||
--color-orange-700: #c4320a;
|
||||
--color-orange-800: #9c2a10;
|
||||
--color-orange-900: #7e2410;
|
||||
--color-orange-950: #511c10;
|
||||
|
||||
--color-success-25: #f6fef9;
|
||||
--color-success-50: #ecfdf3;
|
||||
--color-success-100: #d1fadf;
|
||||
--color-success-200: #a6f4c5;
|
||||
--color-success-300: #6ce9a6;
|
||||
--color-success-400: #32d583;
|
||||
--color-success-500: #12b76a;
|
||||
--color-success-600: #039855;
|
||||
--color-success-700: #027a48;
|
||||
--color-success-800: #05603a;
|
||||
--color-success-900: #054f31;
|
||||
--color-success-950: #053321;
|
||||
|
||||
--color-error-25: #fffbfa;
|
||||
--color-error-50: #fef3f2;
|
||||
--color-error-100: #fee4e2;
|
||||
--color-error-200: #fecdca;
|
||||
--color-error-300: #fda29b;
|
||||
--color-error-400: #f97066;
|
||||
--color-error-500: #f04438;
|
||||
--color-error-600: #d92d20;
|
||||
--color-error-700: #b42318;
|
||||
--color-error-800: #912018;
|
||||
--color-error-900: #7a271a;
|
||||
--color-error-950: #55160c;
|
||||
|
||||
--color-warning-25: #fffcf5;
|
||||
--color-warning-50: #fffaeb;
|
||||
--color-warning-100: #fef0c7;
|
||||
--color-warning-200: #fedf89;
|
||||
--color-warning-300: #fec84b;
|
||||
--color-warning-400: #fdb022;
|
||||
--color-warning-500: #f79009;
|
||||
--color-warning-600: #dc6803;
|
||||
--color-warning-700: #b54708;
|
||||
--color-warning-800: #93370d;
|
||||
--color-warning-900: #7a2e0e;
|
||||
--color-warning-950: #4e1d09;
|
||||
|
||||
--color-theme-pink-500: #ee46bc;
|
||||
|
||||
--color-theme-purple-500: #7a5af8;
|
||||
|
||||
--shadow-theme-md: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
--shadow-theme-lg: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
--shadow-theme-sm: 0px 1px 3px 0px rgba(16, 24, 40, 0.1), 0px 1px 2px 0px rgba(16, 24, 40, 0.06);
|
||||
--shadow-theme-xs: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
|
||||
--shadow-theme-xl: 0px 20px 24px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 8px 8px -4px rgba(16, 24, 40, 0.03);
|
||||
--shadow-datepicker: -5px 0 0 #262d3c, 5px 0 0 #262d3c;
|
||||
--shadow-focus-ring: 0px 0px 0px 4px rgba(70, 95, 255, 0.12);
|
||||
--shadow-slider-navigation: 0px 1px 2px 0px rgba(16, 24, 40, 0.1),
|
||||
0px 1px 3px 0px rgba(16, 24, 40, 0.1);
|
||||
--shadow-tooltip: 0px 4px 6px -2px rgba(16, 24, 40, 0.05),
|
||||
-8px 0px 20px 8px rgba(16, 24, 40, 0.05);
|
||||
|
||||
--drop-shadow-4xl: 0 35px 35px rgba(0, 0, 0, 0.25), 0 45px 65px rgba(0, 0, 0, 0.15);
|
||||
|
||||
--z-index-1: 1;
|
||||
--z-index-9: 9;
|
||||
--z-index-99: 99;
|
||||
--z-index-999: 999;
|
||||
--z-index-9999: 9999;
|
||||
--z-index-99999: 99999;
|
||||
--z-index-999999: 999999;
|
||||
}
|
||||
|
||||
/*
|
||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-gray-200, currentColor);
|
||||
}
|
||||
button:not(:disabled),
|
||||
[role='button']:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
body {
|
||||
@apply relative font-normal font-outfit z-1 bg-gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
@utility menu-item {
|
||||
@apply relative flex items-center w-full gap-3 px-3 py-2 font-medium rounded-lg text-theme-sm;
|
||||
}
|
||||
|
||||
@utility menu-item-active {
|
||||
@apply bg-brand-50 text-brand-500 dark:bg-brand-500/[0.12] dark:text-brand-400;
|
||||
}
|
||||
|
||||
@utility menu-item-inactive {
|
||||
@apply text-gray-700 hover:bg-gray-100 group-hover:text-gray-700 dark:text-gray-300 dark:hover:bg-white/5 dark:hover:text-gray-300;
|
||||
}
|
||||
|
||||
@utility menu-item-icon {
|
||||
@apply text-gray-500 group-hover:text-gray-700 dark:text-gray-400;
|
||||
}
|
||||
|
||||
@utility menu-item-icon-active {
|
||||
@apply text-brand-500 dark:text-brand-400;
|
||||
}
|
||||
|
||||
@utility menu-item-icon-inactive {
|
||||
@apply text-gray-500 group-hover:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-300;
|
||||
}
|
||||
|
||||
@utility menu-item-arrow {
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
@utility menu-item-arrow-active {
|
||||
@apply rotate-180 text-brand-500 dark:text-brand-400;
|
||||
}
|
||||
|
||||
@utility menu-item-arrow-inactive {
|
||||
@apply text-gray-500 group-hover:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-300;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-item {
|
||||
@apply relative flex items-center gap-3 rounded-lg px-3 py-2.5 text-theme-sm font-medium;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-item-active {
|
||||
@apply bg-brand-50 text-brand-500 dark:bg-brand-500/[0.12] dark:text-brand-400;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-item-inactive {
|
||||
@apply text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-white/5;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-item {
|
||||
@apply text-theme-sm relative flex items-center gap-3 rounded-lg px-3 py-2.5 font-medium;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-item-active {
|
||||
@apply bg-brand-50 text-brand-500 dark:bg-brand-500/[0.12] dark:text-brand-400;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-item-inactive {
|
||||
@apply text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-white/5;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-badge {
|
||||
@apply text-success-600 dark:text-success-500 block rounded-full px-2.5 py-0.5 text-xs font-medium uppercase;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-badge-active {
|
||||
@apply bg-success-100 dark:bg-success-500/20;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-badge-inactive {
|
||||
@apply bg-success-50 group-hover:bg-success-100 dark:bg-success-500/15 dark:group-hover:bg-success-500/20;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-badge-pro {
|
||||
@apply text-brand-600 dark:text-brand-500 block rounded-full px-2.5 py-0.5 text-xs font-medium uppercase;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-badge-pro-active {
|
||||
@apply bg-brand-100 dark:bg-brand-500/20;
|
||||
}
|
||||
|
||||
@utility menu-dropdown-badge-pro-inactive {
|
||||
@apply bg-brand-50 group-hover:bg-brand-100 dark:bg-brand-500/15 dark:group-hover:bg-brand-500/20;
|
||||
}
|
||||
|
||||
@utility no-scrollbar {
|
||||
/* Chrome, Safari and Opera */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
@utility custom-scrollbar {
|
||||
&::-webkit-scrollbar {
|
||||
@apply size-1.5;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply rounded-full;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply bg-gray-200 rounded-full dark:bg-gray-700;
|
||||
}
|
||||
}
|
||||
.dark .custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: #344054;
|
||||
}
|
||||
@layer utilities {
|
||||
/* For Remove Date Icon */
|
||||
input[type='date']::-webkit-inner-spin-button,
|
||||
input[type='time']::-webkit-inner-spin-button,
|
||||
input[type='date']::-webkit-calendar-picker-indicator,
|
||||
input[type='time']::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar:hover {
|
||||
width: 290px;
|
||||
}
|
||||
.sidebar:hover .logo {
|
||||
display: block;
|
||||
}
|
||||
.sidebar:hover .logo-icon {
|
||||
display: none;
|
||||
}
|
||||
.sidebar:hover .sidebar-header {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.sidebar:hover .menu-group-title {
|
||||
display: block;
|
||||
}
|
||||
.sidebar:hover .menu-group-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar:hover .menu-item-text {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.sidebar:hover .menu-item-arrow {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar:hover .menu-dropdown {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tableCheckbox:checked ~ span span {
|
||||
@apply opacity-100;
|
||||
}
|
||||
.tableCheckbox:checked ~ span {
|
||||
@apply border-brand-500 bg-brand-500;
|
||||
}
|
||||
|
||||
/* third-party libraries CSS */
|
||||
.apexcharts-legend-text {
|
||||
@apply !text-gray-700 dark:!text-gray-400;
|
||||
}
|
||||
|
||||
.apexcharts-text {
|
||||
@apply !fill-gray-700 dark:!fill-gray-400;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip.apexcharts-theme-light {
|
||||
@apply gap-1 !rounded-lg !border-gray-200 p-3 !shadow-theme-sm dark:!border-gray-800 dark:!bg-gray-900;
|
||||
}
|
||||
|
||||
/* .apexcharts-tooltip-marker {
|
||||
@apply !mr-1.5 !h-1.5 !w-1.5;
|
||||
} */
|
||||
.apexcharts-legend-text {
|
||||
@apply !pl-5 !text-gray-700 dark:!text-gray-400;
|
||||
}
|
||||
.apexcharts-tooltip-series-group {
|
||||
@apply !p-0;
|
||||
}
|
||||
.apexcharts-tooltip-y-group {
|
||||
@apply !p-0;
|
||||
}
|
||||
.apexcharts-tooltip-title {
|
||||
@apply !mb-0 !border-b-0 !bg-transparent !p-0 !text-[10px] !leading-4 !text-gray-800 dark:!text-white/90;
|
||||
}
|
||||
.apexcharts-tooltip-text {
|
||||
@apply !text-theme-xs !text-gray-700 dark:!text-white/90;
|
||||
}
|
||||
.apexcharts-tooltip-text-y-value {
|
||||
@apply !font-medium;
|
||||
}
|
||||
|
||||
.apexcharts-gridline {
|
||||
@apply !stroke-gray-100 dark:!stroke-gray-800;
|
||||
}
|
||||
#chartTwo .apexcharts-datalabels-group {
|
||||
@apply !-translate-y-24;
|
||||
}
|
||||
|
||||
#chartSeven .apexcharts-datalabels-group .apexcharts-text,
|
||||
#chartTwo .apexcharts-datalabels-group .apexcharts-text,
|
||||
#chartThirteen .apexcharts-datalabels-group .apexcharts-text,
|
||||
#chartTwelve .apexcharts-datalabels-group .apexcharts-text {
|
||||
@apply !fill-gray-800 !font-semibold dark:!fill-white/90;
|
||||
}
|
||||
|
||||
#chartSixteen .apexcharts-legend {
|
||||
@apply !p-0 !pl-6;
|
||||
}
|
||||
|
||||
.jvm-container {
|
||||
@apply !bg-gray-50 dark:!bg-gray-900;
|
||||
}
|
||||
.jvm-region.jvm-element {
|
||||
@apply !fill-gray-300 hover:!fill-brand-500 dark:!fill-gray-700 dark:hover:!fill-brand-500;
|
||||
}
|
||||
.jvm-marker.jvm-element {
|
||||
@apply !stroke-gray-200 dark:!stroke-gray-800;
|
||||
}
|
||||
|
||||
.stocks-slider-outer .swiper-button-next:after,
|
||||
.stocks-slider-outer .swiper-button-prev:after {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.stocks-slider-outer .swiper-button-next,
|
||||
.stocks-slider-outer .swiper-button-prev {
|
||||
@apply static! mt-0 h-8 w-9 rounded-full border border-gray-200 !text-gray-700 transition hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-800 dark:text-gray-400!;
|
||||
}
|
||||
|
||||
.stocks-slider-outer .swiper-button-next.swiper-button-disabled,
|
||||
.stocks-slider-outer .swiper-button-prev.swiper-button-disabled {
|
||||
@apply bg-white opacity-50 dark:bg-gray-900;
|
||||
}
|
||||
|
||||
.stocks-slider-outer .swiper-button-next svg,
|
||||
.stocks-slider-outer .swiper-button-prev svg {
|
||||
@apply !h-auto !w-auto;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.flatpickr-wrapper {
|
||||
@apply w-full;
|
||||
}
|
||||
.flatpickr-calendar {
|
||||
@apply mt-2 !rounded-xl bg-black !p-5 !border !border-transparent dark:!border-white/5 !text-gray-500 dark:!bg-gray-dark dark:!text-gray-400 dark:!shadow-theme-xl 2xsm:!w-auto;
|
||||
}
|
||||
.flatpickr-time input {
|
||||
background-color: #f9fafb !important;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||
@apply stroke-brand-500;
|
||||
}
|
||||
.flatpickr-calendar.arrowTop:before,
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
@apply hidden;
|
||||
}
|
||||
.flatpickr-current-month .cur-month,
|
||||
.flatpickr-current-month input.cur-year {
|
||||
@apply !h-auto !pt-0 !text-lg !font-medium !text-gray-800 dark:!text-white/90;
|
||||
}
|
||||
|
||||
.flatpickr-prev-month,
|
||||
.flatpickr-next-month {
|
||||
@apply !p-0;
|
||||
}
|
||||
|
||||
.flatpickr-weekdays {
|
||||
@apply h-auto mt-6 mb-4;
|
||||
}
|
||||
|
||||
.flatpickr-weekday {
|
||||
@apply !text-theme-sm !font-medium !text-gray-500 dark:!text-gray-400;
|
||||
}
|
||||
|
||||
.flatpickr-day {
|
||||
@apply !flex !items-center !text-theme-sm !font-medium !text-gray-800 dark:!text-white/90 dark:hover:!border-gray-300 dark:hover:!bg-gray-900;
|
||||
}
|
||||
.flatpickr-day.nextMonthDay,
|
||||
.flatpickr-day.prevMonthDay {
|
||||
@apply !text-gray-400;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month {
|
||||
@apply !top-7 dark:!fill-white dark:!text-white;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-prev-month {
|
||||
@apply !left-7;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,
|
||||
.flatpickr-months .flatpickr-next-month.flatpickr-next-month {
|
||||
@apply !right-7;
|
||||
}
|
||||
span.flatpickr-weekday,
|
||||
.flatpickr-months .flatpickr-month {
|
||||
@apply dark:!fill-white dark:!text-white;
|
||||
}
|
||||
.flatpickr-day.inRange {
|
||||
box-shadow:
|
||||
-5px 0 0 #f9fafb,
|
||||
5px 0 0 #f9fafb !important;
|
||||
@apply dark:!shadow-datepicker;
|
||||
}
|
||||
.flatpickr-day.inRange,
|
||||
.flatpickr-day.prevMonthDay.inRange,
|
||||
.flatpickr-day.nextMonthDay.inRange,
|
||||
.flatpickr-day.today.inRange,
|
||||
.flatpickr-day.prevMonthDay.today.inRange,
|
||||
.flatpickr-day.nextMonthDay.today.inRange,
|
||||
.flatpickr-day:hover,
|
||||
.flatpickr-day.prevMonthDay:hover,
|
||||
.flatpickr-day.nextMonthDay:hover,
|
||||
.flatpickr-day:focus,
|
||||
.flatpickr-day.prevMonthDay:focus,
|
||||
.flatpickr-day.nextMonthDay:focus {
|
||||
@apply !border-gray-50 !bg-gray-50 dark:!border-0 dark:!border-white/5 dark:!bg-white/5;
|
||||
}
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.endRange {
|
||||
@apply !text-white dark:!text-white;
|
||||
}
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
background: #465fff;
|
||||
@apply !border-brand-500 !bg-brand-500 hover:!border-brand-500 hover:!bg-brand-500;
|
||||
}
|
||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n + 1)),
|
||||
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n + 1)),
|
||||
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n + 1)) {
|
||||
box-shadow: -10px 0 0 #465fff;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month svg,
|
||||
.flatpickr-months .flatpickr-next-month svg,
|
||||
.flatpickr-months .flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month {
|
||||
@apply hover:!fill-none;
|
||||
}
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||
fill: none !important;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.static {
|
||||
@apply right-0;
|
||||
}
|
||||
.flatpickr-calendar.hasTime {
|
||||
width: 300px !important;
|
||||
}
|
||||
.flatpickr-calendar.hasTime .flatpickr-time {
|
||||
border: transparent !important;
|
||||
}
|
||||
.fc .fc-view-harness {
|
||||
@apply max-w-full overflow-x-auto custom-scrollbar;
|
||||
}
|
||||
.fc-dayGridMonth-view.fc-view.fc-daygrid {
|
||||
@apply min-w-[718px];
|
||||
}
|
||||
.fc .fc-scrollgrid-section > * {
|
||||
border-right-width: 0;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.fc .fc-scrollgrid {
|
||||
border-left-width: 0;
|
||||
}
|
||||
.fc .fc-toolbar.fc-header-toolbar {
|
||||
@apply flex-col gap-4 px-6 pt-6 sm:flex-row;
|
||||
}
|
||||
.fc-button-group {
|
||||
@apply gap-2;
|
||||
}
|
||||
.fc-button-group .fc-button {
|
||||
@apply flex h-10 w-10 items-center justify-center !rounded-lg border border-gray-200 bg-transparent hover:border-gray-200 hover:bg-gray-50 focus:shadow-none active:!border-gray-200 active:!bg-transparent active:!shadow-none dark:border-gray-800 dark:hover:border-gray-800 dark:hover:bg-gray-900 dark:active:!border-gray-800;
|
||||
}
|
||||
|
||||
.fc-button-group .fc-button.fc-prev-button:before {
|
||||
@apply inline-block mt-1;
|
||||
content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16.0068 6L9.75684 12.25L16.0068 18.5' stroke='%23344054' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
.fc-button-group .fc-button.fc-next-button:before {
|
||||
@apply inline-block mt-1;
|
||||
content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.50684 19L15.7568 12.75L9.50684 6.5' stroke='%23344054' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
.dark .fc-button-group .fc-button.fc-prev-button:before {
|
||||
content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16.0068 6L9.75684 12.25L16.0068 18.5' stroke='%2398A2B3' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
.dark .fc-button-group .fc-button.fc-next-button:before {
|
||||
content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.50684 19L15.7568 12.75L9.50684 6.5' stroke='%2398A2B3' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
.fc-button-group .fc-button .fc-icon {
|
||||
@apply hidden;
|
||||
}
|
||||
.fc-addEventButton-button {
|
||||
@apply !rounded-lg !border-0 !bg-brand-500 !px-4 !py-2.5 !text-sm !font-medium hover:!bg-brand-600 focus:!shadow-none;
|
||||
}
|
||||
.fc-toolbar-title {
|
||||
@apply !text-lg !font-medium text-gray-800 dark:text-white/90;
|
||||
}
|
||||
.fc-header-toolbar.fc-toolbar .fc-toolbar-chunk:last-child {
|
||||
@apply rounded-lg bg-gray-100 p-0.5 dark:bg-gray-900;
|
||||
}
|
||||
.fc-header-toolbar.fc-toolbar .fc-toolbar-chunk:last-child .fc-button {
|
||||
@apply !h-auto !w-auto rounded-md !border-0 bg-transparent !px-5 !py-2 text-sm font-medium text-gray-500 hover:text-gray-700 focus:!shadow-none dark:text-gray-400;
|
||||
}
|
||||
.fc-header-toolbar.fc-toolbar .fc-toolbar-chunk:last-child .fc-button.fc-button-active {
|
||||
@apply text-gray-900 bg-white dark:bg-gray-800 dark:text-white;
|
||||
}
|
||||
.fc-theme-standard th {
|
||||
@apply !border-x-0 border-t !border-gray-200 bg-gray-50 !text-left dark:!border-gray-800 dark:bg-gray-900;
|
||||
}
|
||||
.fc-theme-standard td,
|
||||
.fc-theme-standard .fc-scrollgrid {
|
||||
@apply !border-gray-200 dark:!border-gray-800;
|
||||
}
|
||||
.fc .fc-col-header-cell-cushion {
|
||||
@apply !px-5 !py-4 text-sm font-medium uppercase text-gray-400;
|
||||
}
|
||||
.fc .fc-daygrid-day.fc-day-today {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
.fc .fc-daygrid-day {
|
||||
@apply p-2;
|
||||
}
|
||||
.fc .fc-daygrid-day.fc-day-today .fc-scrollgrid-sync-inner {
|
||||
@apply rounded-sm bg-gray-100 dark:bg-white/[0.03];
|
||||
}
|
||||
.fc .fc-daygrid-day-number {
|
||||
@apply !p-3 text-sm font-medium text-gray-700 dark:text-gray-400;
|
||||
}
|
||||
.fc .fc-daygrid-day-top {
|
||||
@apply flex-row!;
|
||||
}
|
||||
.fc .fc-day-other .fc-daygrid-day-top {
|
||||
opacity: 1;
|
||||
}
|
||||
.fc .fc-day-other .fc-daygrid-day-top .fc-daygrid-day-number {
|
||||
@apply text-gray-400 dark:text-white/30;
|
||||
}
|
||||
.event-fc-color {
|
||||
@apply rounded-lg py-2.5 pl-4 pr-3;
|
||||
}
|
||||
.event-fc-color .fc-event-title {
|
||||
@apply p-0 text-sm font-normal text-gray-700;
|
||||
}
|
||||
.fc-daygrid-event-dot {
|
||||
@apply w-1 h-5 ml-0 mr-3 border-none rounded-sm;
|
||||
}
|
||||
.fc-event {
|
||||
@apply focus:shadow-none;
|
||||
}
|
||||
.fc-daygrid-event.fc-event-start {
|
||||
@apply !ml-3;
|
||||
}
|
||||
.event-fc-color.fc-bg-success {
|
||||
@apply border-success-50 bg-success-50;
|
||||
}
|
||||
.event-fc-color.fc-bg-danger {
|
||||
@apply border-error-50 bg-error-50;
|
||||
}
|
||||
.event-fc-color.fc-bg-primary {
|
||||
@apply border-brand-50 bg-brand-50;
|
||||
}
|
||||
.event-fc-color.fc-bg-warning {
|
||||
@apply border-orange-50 bg-orange-50;
|
||||
}
|
||||
.event-fc-color.fc-bg-success .fc-daygrid-event-dot {
|
||||
@apply bg-success-500;
|
||||
}
|
||||
.event-fc-color.fc-bg-danger .fc-daygrid-event-dot {
|
||||
@apply bg-error-500;
|
||||
}
|
||||
.event-fc-color.fc-bg-primary .fc-daygrid-event-dot {
|
||||
@apply bg-brand-500;
|
||||
}
|
||||
.event-fc-color.fc-bg-warning .fc-daygrid-event-dot {
|
||||
@apply bg-orange-500;
|
||||
}
|
||||
.fc-direction-ltr .fc-timegrid-slot-label-frame {
|
||||
@apply px-3 py-1.5 text-left text-sm font-medium text-gray-500 dark:text-gray-400;
|
||||
}
|
||||
.fc .fc-timegrid-axis-cushion {
|
||||
@apply text-sm font-medium text-gray-500 dark:text-gray-400;
|
||||
}
|
||||
|
||||
.input-date-icon::-webkit-inner-spin-button,
|
||||
.input-date-icon::-webkit-calendar-picker-indicator {
|
||||
opacity: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.swiper-button-prev svg,
|
||||
.swiper-button-next svg {
|
||||
@apply !h-auto w-auto!;
|
||||
}
|
||||
|
||||
.carouselTwo .swiper-button-next:after,
|
||||
.carouselTwo .swiper-button-prev:after,
|
||||
.carouselFour .swiper-button-next:after,
|
||||
.carouselFour .swiper-button-prev:after {
|
||||
@apply hidden;
|
||||
}
|
||||
.carouselTwo .swiper-button-next.swiper-button-disabled,
|
||||
.carouselTwo .swiper-button-prev.swiper-button-disabled,
|
||||
.carouselFour .swiper-button-next.swiper-button-disabled,
|
||||
.carouselFour .swiper-button-prev.swiper-button-disabled {
|
||||
@apply bg-white/60 opacity-100!;
|
||||
}
|
||||
.carouselTwo .swiper-button-next,
|
||||
.carouselTwo .swiper-button-prev,
|
||||
.carouselFour .swiper-button-next,
|
||||
.carouselFour .swiper-button-prev {
|
||||
@apply h-10 w-10 rounded-full border-[0.5px] border-white/10 bg-white/90 !text-gray-700 shadow-slider-navigation backdrop-blur-[10px];
|
||||
}
|
||||
|
||||
.carouselTwo .swiper-button-prev,
|
||||
.carouselFour .swiper-button-prev {
|
||||
@apply !left-3 sm:!left-4;
|
||||
}
|
||||
|
||||
.carouselTwo .swiper-button-next,
|
||||
.carouselFour .swiper-button-next {
|
||||
@apply !right-3 sm:!right-4;
|
||||
}
|
||||
|
||||
.carouselThree .swiper-pagination,
|
||||
.carouselFour .swiper-pagination {
|
||||
@apply !bottom-3 !left-1/2 inline-flex !w-auto -translate-x-1/2 items-center gap-1.5 rounded-[40px] border-[0.5px] border-white/10 bg-white/60 px-2 py-1.5 shadow-slider-navigation backdrop-blur-[10px] sm:!bottom-5;
|
||||
}
|
||||
|
||||
.carouselThree .swiper-pagination-bullet,
|
||||
.carouselFour .swiper-pagination-bullet {
|
||||
@apply !m-0 h-2.5 w-2.5 bg-white opacity-100 shadow-theme-xs duration-200 ease-in-out;
|
||||
}
|
||||
|
||||
.carouselThree .swiper-pagination-bullet-active,
|
||||
.carouselFour .swiper-pagination-bullet-active {
|
||||
@apply w-6.5 rounded-xl;
|
||||
}
|
||||
|
||||
.form-check-input:checked ~ span {
|
||||
@apply border-[6px] border-brand-500 dark:border-brand-500;
|
||||
}
|
||||
|
||||
.taskCheckbox:checked ~ .box span {
|
||||
@apply opacity-100;
|
||||
}
|
||||
.taskCheckbox:checked ~ p {
|
||||
@apply text-gray-400 line-through;
|
||||
}
|
||||
.taskCheckbox:checked ~ .box {
|
||||
@apply border-brand-500 bg-brand-500 dark:border-brand-500;
|
||||
}
|
||||
|
||||
.task {
|
||||
transition: all 0.2s ease; /* Smooth transition for visual effects */
|
||||
}
|
||||
|
||||
.task.is-dragging {
|
||||
border-radius: 0.75rem;
|
||||
box-shadow:
|
||||
0px 1px 3px 0px rgba(16, 24, 40, 0.1),
|
||||
0px 1px 2px 0px rgba(16, 24, 40, 0.06);
|
||||
opacity: 0.8;
|
||||
cursor: grabbing; /* Changes the cursor to indicate dragging */
|
||||
}
|
||||
|
||||
|
||||
.custom-calendar .fc-h-event {
|
||||
background-color: #0000;
|
||||
border: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar:before {
|
||||
@apply !bg-gray-200 !rounded-full dark:!bg-gray-700 !opacity-100;
|
||||
}
|
||||
|
||||
.dark .simplebar-scrollbar::before {
|
||||
@apply !bg-gray-700;
|
||||
}
|
||||
|
||||
.simplebar-scrollbar.simplebar-visible:before {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.social-button {
|
||||
@apply flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
@apply flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 lg:inline-flex lg:w-auto;
|
||||
}
|
||||
|
||||
/* Code Editor */
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
text-shadow: none;
|
||||
color: #344054;
|
||||
overflow: hidden !important;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.dark code[class*='language-'],
|
||||
.dark pre[class*='language-'] {
|
||||
text-shadow: none;
|
||||
color: #98a2b3;
|
||||
}
|
||||
.language-html {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
.dark .language-html {
|
||||
background-color: #ffffff00 !important;
|
||||
}
|
||||
.token {
|
||||
text-shadow: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
.token.doctype-tag,
|
||||
.token.name {
|
||||
color: #018001;
|
||||
}
|
||||
.token.tag {
|
||||
color: #267f99;
|
||||
}
|
||||
.token.selector {
|
||||
color: #267f99;
|
||||
}
|
||||
.token.property {
|
||||
color: #0070c1;
|
||||
}
|
||||
.token.language-css {
|
||||
color: #1b00ff;
|
||||
}
|
||||
.token.attr-name {
|
||||
color: #98a2b3;
|
||||
}
|
||||
.token.attr-value {
|
||||
color: #a31615;
|
||||
}
|
||||
.token.punctuation {
|
||||
color: #344054;
|
||||
}
|
||||
.dark .token.punctuation {
|
||||
color: #98a2b3;
|
||||
}
|
||||
.custom-datepicker .flatpickr-calendar.static.open {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.swiper-button-prev, .swiper-button-next {
|
||||
svg {
|
||||
fill: none !important;
|
||||
}
|
||||
}
|
||||
51
resources/js/app.js
Normal file
51
resources/js/app.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import './bootstrap';
|
||||
import Alpine from 'alpinejs';
|
||||
import ApexCharts from 'apexcharts';
|
||||
|
||||
// flatpickr
|
||||
import flatpickr from 'flatpickr';
|
||||
import 'flatpickr/dist/flatpickr.min.css';
|
||||
// FullCalendar
|
||||
import { Calendar } from '@fullcalendar/core';
|
||||
|
||||
|
||||
|
||||
window.Alpine = Alpine;
|
||||
window.ApexCharts = ApexCharts;
|
||||
window.flatpickr = flatpickr;
|
||||
window.FullCalendar = Calendar;
|
||||
|
||||
Alpine.start();
|
||||
|
||||
// Initialize components on DOM ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Map imports
|
||||
if (document.querySelector('#mapOne')) {
|
||||
import('./components/map').then(module => module.initMap());
|
||||
}
|
||||
|
||||
// Chart imports
|
||||
if (document.querySelector('#chartOne')) {
|
||||
import('./components/chart/chart-1').then(module => module.initChartOne());
|
||||
}
|
||||
if (document.querySelector('#chartTwo')) {
|
||||
import('./components/chart/chart-2').then(module => module.initChartTwo());
|
||||
}
|
||||
if (document.querySelector('#chartThree')) {
|
||||
import('./components/chart/chart-3').then(module => module.initChartThree());
|
||||
}
|
||||
if (document.querySelector('#chartSix')) {
|
||||
import('./components/chart/chart-6').then(module => module.initChartSix());
|
||||
}
|
||||
if (document.querySelector('#chartEight')) {
|
||||
import('./components/chart/chart-8').then(module => module.initChartEight());
|
||||
}
|
||||
if (document.querySelector('#chartThirteen')) {
|
||||
import('./components/chart/chart-13').then(module => module.initChartThirteen());
|
||||
}
|
||||
|
||||
// Calendar init
|
||||
if (document.querySelector('#calendar')) {
|
||||
import('./components/calendar-init').then(module => module.calendarInit());
|
||||
}
|
||||
});
|
||||
4
resources/js/bootstrap.js
vendored
Normal file
4
resources/js/bootstrap.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import axios from 'axios';
|
||||
window.axios = axios;
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
352
resources/js/components/calendar-init.js
Normal file
352
resources/js/components/calendar-init.js
Normal file
@@ -0,0 +1,352 @@
|
||||
|
||||
|
||||
import { Calendar } from "@fullcalendar/core";
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
import listPlugin from "@fullcalendar/list";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
|
||||
export function calendarInit() {
|
||||
const calendarWrapper = document.querySelector("#calendar");
|
||||
|
||||
if (calendarWrapper) {
|
||||
// Calendar Date variable
|
||||
const newDate = new Date();
|
||||
const getDynamicMonth = () => {
|
||||
const month = newDate.getMonth() + 1;
|
||||
return month < 10 ? `0${month}` : `${month}`;
|
||||
};
|
||||
|
||||
// Calendar Modal Elements
|
||||
const getModalTitleEl = document.querySelector("#event-title");
|
||||
const getModalStartDateEl = document.querySelector("#event-start-date");
|
||||
const getModalEndDateEl = document.querySelector("#event-end-date");
|
||||
const getModalAddBtnEl = document.querySelector(".btn-add-event");
|
||||
const getModalUpdateBtnEl = document.querySelector(".btn-update-event");
|
||||
const getModalHeaderEl = document.querySelector("#eventModalLabel");
|
||||
|
||||
const calendarsEvents = {
|
||||
Danger: "danger",
|
||||
Success: "success",
|
||||
Primary: "primary",
|
||||
Warning: "warning",
|
||||
};
|
||||
|
||||
// Calendar Elements and options
|
||||
const calendarEl = document.querySelector("#calendar");
|
||||
|
||||
const calendarHeaderToolbar = {
|
||||
left: "prev,next addEventButton",
|
||||
center: "title",
|
||||
right: "dayGridMonth,timeGridWeek,timeGridDay",
|
||||
};
|
||||
|
||||
const calendarEventsList = [
|
||||
{
|
||||
id: "1",
|
||||
title: "Event Conf.",
|
||||
start: `${newDate.getFullYear()}-${getDynamicMonth()}-01`,
|
||||
extendedProps: { calendar: "Danger" },
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Seminar #4",
|
||||
start: `${newDate.getFullYear()}-${getDynamicMonth()}-07`,
|
||||
end: `${newDate.getFullYear()}-${getDynamicMonth()}-10`,
|
||||
extendedProps: { calendar: "Success" },
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "Meeting #5",
|
||||
start: `${newDate.getFullYear()}-${getDynamicMonth()}-09T16:00:00`,
|
||||
extendedProps: { calendar: "Primary" },
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
title: "Submission #1",
|
||||
start: `${newDate.getFullYear()}-${getDynamicMonth()}-16T16:00:00`,
|
||||
extendedProps: { calendar: "Warning" },
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
title: "Seminar #6",
|
||||
start: `${newDate.getFullYear()}-${getDynamicMonth()}-11`,
|
||||
end: `${newDate.getFullYear()}-${getDynamicMonth()}-13`,
|
||||
extendedProps: { calendar: "Danger" },
|
||||
},
|
||||
];
|
||||
|
||||
// Modal Functions
|
||||
const openModal = () => {
|
||||
const modal = document.getElementById("eventModal");
|
||||
if (modal) {
|
||||
modal.style.display = "flex";
|
||||
document.body.style.overflow = "hidden"; // Prevent background scroll
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
const modal = document.getElementById("eventModal");
|
||||
if (modal) {
|
||||
modal.style.display = "none";
|
||||
document.body.style.overflow = ""; // Restore scroll
|
||||
}
|
||||
resetModalFields();
|
||||
};
|
||||
|
||||
// Reset modal fields
|
||||
function resetModalFields() {
|
||||
if (getModalTitleEl) getModalTitleEl.value = "";
|
||||
if (getModalStartDateEl) getModalStartDateEl.value = "";
|
||||
if (getModalEndDateEl) getModalEndDateEl.value = "";
|
||||
|
||||
const getModalIfCheckedRadioBtnEl = document.querySelector(
|
||||
'input[name="event-level"]:checked'
|
||||
);
|
||||
if (getModalIfCheckedRadioBtnEl) {
|
||||
getModalIfCheckedRadioBtnEl.checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Calendar Select function (when user clicks/drags on calendar)
|
||||
const calendarSelect = (info) => {
|
||||
resetModalFields();
|
||||
|
||||
// Update modal header
|
||||
if (getModalHeaderEl) {
|
||||
getModalHeaderEl.textContent = "Add Event";
|
||||
}
|
||||
|
||||
// Show Add button, hide Update button
|
||||
if (getModalAddBtnEl) getModalAddBtnEl.style.display = "flex";
|
||||
if (getModalUpdateBtnEl) getModalUpdateBtnEl.style.display = "none";
|
||||
|
||||
// Set dates from selection
|
||||
if (getModalStartDateEl) getModalStartDateEl.value = info.startStr;
|
||||
if (getModalEndDateEl) {
|
||||
getModalEndDateEl.value = info.endStr || info.startStr;
|
||||
}
|
||||
|
||||
openModal();
|
||||
};
|
||||
|
||||
// Calendar AddEvent button click
|
||||
const calendarAddEvent = () => {
|
||||
resetModalFields();
|
||||
|
||||
// Update modal header
|
||||
if (getModalHeaderEl) {
|
||||
getModalHeaderEl.textContent = "Add Event";
|
||||
}
|
||||
|
||||
// Show Add button, hide Update button
|
||||
if (getModalAddBtnEl) getModalAddBtnEl.style.display = "flex";
|
||||
if (getModalUpdateBtnEl) getModalUpdateBtnEl.style.display = "none";
|
||||
|
||||
// Set default start date to today
|
||||
const currentDate = new Date();
|
||||
const yyyy = currentDate.getFullYear();
|
||||
const mm = String(currentDate.getMonth() + 1).padStart(2, "0");
|
||||
const dd = String(currentDate.getDate()).padStart(2, "0");
|
||||
const combineDate = `${yyyy}-${mm}-${dd}`;
|
||||
|
||||
if (getModalStartDateEl) getModalStartDateEl.value = combineDate;
|
||||
|
||||
openModal();
|
||||
};
|
||||
|
||||
// Calendar Event Click function (when user clicks existing event)
|
||||
const calendarEventClick = (info) => {
|
||||
const eventObj = info.event;
|
||||
|
||||
if (eventObj.url) {
|
||||
window.open(eventObj.url);
|
||||
info.jsEvent.preventDefault();
|
||||
} else {
|
||||
resetModalFields();
|
||||
|
||||
// Update modal header
|
||||
if (getModalHeaderEl) {
|
||||
getModalHeaderEl.textContent = "Edit Event";
|
||||
}
|
||||
|
||||
// Get event details
|
||||
const getModalEventId = eventObj.id;
|
||||
const getModalEventLevel = eventObj.extendedProps.calendar;
|
||||
|
||||
// Set form values
|
||||
if (getModalTitleEl) getModalTitleEl.value = eventObj.title;
|
||||
if (getModalStartDateEl) {
|
||||
getModalStartDateEl.value = eventObj.startStr.split("T")[0];
|
||||
}
|
||||
if (getModalEndDateEl) {
|
||||
getModalEndDateEl.value = eventObj.endStr
|
||||
? eventObj.endStr.split("T")[0]
|
||||
: "";
|
||||
}
|
||||
|
||||
// Check the correct radio button
|
||||
const getModalCheckedRadioBtnEl = document.querySelector(
|
||||
`input[value="${getModalEventLevel}"]`
|
||||
);
|
||||
if (getModalCheckedRadioBtnEl) {
|
||||
getModalCheckedRadioBtnEl.checked = true;
|
||||
}
|
||||
|
||||
// Store event ID for update
|
||||
if (getModalUpdateBtnEl) {
|
||||
getModalUpdateBtnEl.dataset.fcEventPublicId = getModalEventId;
|
||||
}
|
||||
|
||||
// Hide Add button, show Update button
|
||||
if (getModalAddBtnEl) getModalAddBtnEl.style.display = "none";
|
||||
if (getModalUpdateBtnEl) getModalUpdateBtnEl.style.display = "flex";
|
||||
|
||||
openModal();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize Calendar
|
||||
const calendar = new Calendar(calendarEl, {
|
||||
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
|
||||
selectable: true,
|
||||
initialView: "dayGridMonth",
|
||||
initialDate: `${newDate.getFullYear()}-${getDynamicMonth()}-07`,
|
||||
headerToolbar: calendarHeaderToolbar,
|
||||
events: calendarEventsList,
|
||||
select: calendarSelect,
|
||||
eventClick: calendarEventClick,
|
||||
displayEventTime: false, // Hide time display
|
||||
customButtons: {
|
||||
addEventButton: {
|
||||
text: "Add Event +",
|
||||
click: calendarAddEvent,
|
||||
},
|
||||
},
|
||||
// eventClassNames({ event: calendarEvent }) {
|
||||
// const getColorValue =
|
||||
// calendarsEvents[calendarEvent._def.extendedProps.calendar];
|
||||
// return [`event-fc-color`, `fc-bg-${getColorValue}`];
|
||||
// },
|
||||
// Optional: Custom event content without time
|
||||
eventContent(eventInfo) {
|
||||
const colorClass = `fc-bg-${eventInfo.event.extendedProps.calendar.toLowerCase()}`
|
||||
return {
|
||||
html: `
|
||||
<div class="event-fc-color flex fc-event-main ${colorClass} p-1 rounded-sm">
|
||||
<div class="fc-daygrid-event-dot"></div>
|
||||
<div class="fc-event-time">${eventInfo.timeText}</div>
|
||||
<div class="fc-event-title">${eventInfo.event.title}</div>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Update Calendar Event
|
||||
// if (getModalUpdateBtnEl) {
|
||||
// getModalUpdateBtnEl.addEventListener("click", () => {
|
||||
// const getPublicID = getModalUpdateBtnEl.dataset.fcEventPublicId;
|
||||
// const getTitleUpdatedValue = getModalTitleEl.value;
|
||||
// const setModalStartDateValue = getModalStartDateEl.value;
|
||||
// const setModalEndDateValue = getModalEndDateEl.value;
|
||||
// const getEvent = calendar.getEventById(getPublicID);
|
||||
// const getModalUpdatedCheckedRadioBtnEl = document.querySelector(
|
||||
// 'input[name="event-level"]:checked'
|
||||
// );
|
||||
|
||||
// const getModalUpdatedCheckedRadioBtnValue =
|
||||
// getModalUpdatedCheckedRadioBtnEl
|
||||
// ? getModalUpdatedCheckedRadioBtnEl.value
|
||||
// : "";
|
||||
|
||||
// if (getEvent) {
|
||||
// getEvent.setProp("title", getTitleUpdatedValue);
|
||||
// getEvent.setDates(setModalStartDateValue, setModalEndDateValue);
|
||||
// getEvent.setExtendedProp("calendar", getModalUpdatedCheckedRadioBtnValue);
|
||||
// }
|
||||
|
||||
// closeModal();
|
||||
// });
|
||||
// }
|
||||
if (getModalUpdateBtnEl) {
|
||||
getModalUpdateBtnEl.addEventListener("click", () => {
|
||||
const getPublicID = getModalUpdateBtnEl.dataset.fcEventPublicId;
|
||||
const getTitleUpdatedValue = getModalTitleEl.value;
|
||||
const setModalStartDateValue = getModalStartDateEl.value;
|
||||
const setModalEndDateValue = getModalEndDateEl.value;
|
||||
const getEvent = calendar.getEventById(getPublicID);
|
||||
const getModalUpdatedCheckedRadioBtnEl = document.querySelector(
|
||||
'input[name="event-level"]:checked'
|
||||
);
|
||||
|
||||
const getModalUpdatedCheckedRadioBtnValue =
|
||||
getModalUpdatedCheckedRadioBtnEl
|
||||
? getModalUpdatedCheckedRadioBtnEl.value
|
||||
: "";
|
||||
|
||||
if (getEvent) {
|
||||
// Remove the old event
|
||||
getEvent.remove();
|
||||
|
||||
// Add updated event with all properties
|
||||
calendar.addEvent({
|
||||
id: getPublicID,
|
||||
title: getTitleUpdatedValue,
|
||||
start: setModalStartDateValue,
|
||||
end: setModalEndDateValue,
|
||||
allDay: true,
|
||||
extendedProps: { calendar: getModalUpdatedCheckedRadioBtnValue },
|
||||
});
|
||||
}
|
||||
|
||||
closeModal();
|
||||
});
|
||||
}
|
||||
|
||||
// Add Calendar Event
|
||||
if (getModalAddBtnEl) {
|
||||
getModalAddBtnEl.addEventListener("click", () => {
|
||||
const getModalCheckedRadioBtnEl = document.querySelector(
|
||||
'input[name="event-level"]:checked'
|
||||
);
|
||||
|
||||
const getTitleValue = getModalTitleEl.value;
|
||||
const setModalStartDateValue = getModalStartDateEl.value;
|
||||
const setModalEndDateValue = getModalEndDateEl.value;
|
||||
const getModalCheckedRadioBtnValue = getModalCheckedRadioBtnEl
|
||||
? getModalCheckedRadioBtnEl.value
|
||||
: "";
|
||||
|
||||
calendar.addEvent({
|
||||
id: Date.now().toString(),
|
||||
title: getTitleValue,
|
||||
start: setModalStartDateValue,
|
||||
end: setModalEndDateValue,
|
||||
allDay: true,
|
||||
extendedProps: { calendar: getModalCheckedRadioBtnValue },
|
||||
});
|
||||
|
||||
closeModal();
|
||||
});
|
||||
}
|
||||
|
||||
// Render Calendar
|
||||
calendar.render();
|
||||
|
||||
// Close modal event listeners
|
||||
document.querySelectorAll(".modal-close-btn").forEach((btn) => {
|
||||
btn.addEventListener("click", closeModal);
|
||||
});
|
||||
|
||||
// Close when clicking outside modal
|
||||
window.addEventListener("click", (event) => {
|
||||
const modal = document.getElementById("eventModal");
|
||||
if (event.target === modal) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default calendarInit;
|
||||
100
resources/js/components/chart/chart-1.js
Normal file
100
resources/js/components/chart/chart-1.js
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
|
||||
export const initChartOne = () => {
|
||||
const chartElement = document.querySelector('#chartOne');
|
||||
if (!chartElement) return;
|
||||
|
||||
const chartOneOptions = {
|
||||
series: [{
|
||||
name: "Sales",
|
||||
data: [168, 385, 201, 298, 187, 195, 291, 110, 215, 390, 280, 112],
|
||||
},],
|
||||
colors: ["#465fff"],
|
||||
chart: {
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
type: "bar",
|
||||
height: 180,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: "39%",
|
||||
borderRadius: 5,
|
||||
borderRadiusApplication: "end",
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
width: 4,
|
||||
colors: ["transparent"],
|
||||
},
|
||||
xaxis: {
|
||||
categories: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
],
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
fontFamily: "Outfit",
|
||||
markers: {
|
||||
radius: 99,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: false,
|
||||
},
|
||||
grid: {
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
x: {
|
||||
show: false,
|
||||
},
|
||||
y: {
|
||||
formatter: function (val) {
|
||||
return val;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const chart = new ApexCharts(chartElement, chartOneOptions);
|
||||
chart.render();
|
||||
|
||||
return chart;
|
||||
};
|
||||
|
||||
export default initChartOne;
|
||||
332
resources/js/components/chart/chart-13.js
Normal file
332
resources/js/components/chart/chart-13.js
Normal file
@@ -0,0 +1,332 @@
|
||||
|
||||
export function initChartThirteen() {
|
||||
const chartThirteenEl = document.querySelector("#chartThirteen");
|
||||
if (chartThirteenEl) {
|
||||
const data = [
|
||||
[1746153600000, 30.95],
|
||||
[1746240000000, 31.34],
|
||||
[1746326400000, 31.18],
|
||||
[1746412800000, 31.05],
|
||||
[1746672000000, 31.0],
|
||||
[1746758400000, 30.95],
|
||||
[1746844800000, 31.24],
|
||||
[1746931200000, 31.29],
|
||||
[1747017600000, 31.85],
|
||||
[1747276800000, 31.86],
|
||||
[1747363200000, 32.28],
|
||||
[1747449600000, 32.1],
|
||||
[1747536000000, 32.65],
|
||||
[1747622400000, 32.21],
|
||||
[1747881600000, 32.35],
|
||||
[1747968000000, 32.44],
|
||||
[1748054400000, 32.46],
|
||||
[1748140800000, 32.86],
|
||||
[1748227200000, 32.75],
|
||||
[1748572800000, 32.54],
|
||||
[1748659200000, 32.33],
|
||||
[1748745600000, 32.97],
|
||||
[1748832000000, 33.41],
|
||||
[1749091200000, 33.27],
|
||||
[1749177600000, 33.27],
|
||||
[1749264000000, 32.89],
|
||||
[1749350400000, 33.1],
|
||||
[1749436800000, 33.73],
|
||||
[1749696000000, 33.22],
|
||||
[1749782400000, 31.99],
|
||||
[1749868800000, 32.41],
|
||||
[1749955200000, 33.05],
|
||||
[1750041600000, 33.64],
|
||||
[1750300800000, 33.56],
|
||||
[1750387200000, 34.22],
|
||||
[1750473600000, 33.77],
|
||||
[1750560000000, 34.17],
|
||||
[1750646400000, 33.82],
|
||||
[1750905600000, 34.51],
|
||||
[1750992000000, 33.16],
|
||||
[1751078400000, 33.56],
|
||||
[1751164800000, 33.71],
|
||||
[1751251200000, 33.81],
|
||||
[1751506800000, 34.4],
|
||||
[1751593200000, 34.63],
|
||||
[1751679600000, 34.46],
|
||||
[1751766000000, 34.48],
|
||||
[1751852400000, 34.31],
|
||||
[1752111600000, 34.7],
|
||||
[1752198000000, 34.31],
|
||||
[1752284400000, 33.46],
|
||||
[1752370800000, 33.59],
|
||||
[1752716400000, 33.22],
|
||||
[1752802800000, 32.61],
|
||||
[1752889200000, 33.01],
|
||||
[1752975600000, 33.55],
|
||||
[1753062000000, 33.18],
|
||||
[1753321200000, 32.84],
|
||||
[1753407600000, 33.84],
|
||||
[1753494000000, 33.39],
|
||||
[1753580400000, 32.91],
|
||||
[1753666800000, 33.06],
|
||||
[1753926000000, 32.62],
|
||||
[1754012400000, 32.4],
|
||||
[1754098800000, 33.13],
|
||||
[1754185200000, 33.26],
|
||||
[1754271600000, 33.58],
|
||||
[1754530800000, 33.55],
|
||||
[1754617200000, 33.77],
|
||||
[1754703600000, 33.76],
|
||||
[1754790000000, 33.32],
|
||||
[1754876400000, 32.61],
|
||||
[1755135600000, 32.52],
|
||||
[1755222000000, 32.67],
|
||||
[1755308400000, 32.52],
|
||||
[1755394800000, 31.92],
|
||||
[1755481200000, 32.2],
|
||||
[1755740400000, 32.23],
|
||||
[1755826800000, 32.33],
|
||||
[1755913200000, 32.36],
|
||||
[1755999600000, 32.01],
|
||||
[1756086000000, 31.31],
|
||||
[1756345200000, 32.01],
|
||||
[1756431600000, 32.01],
|
||||
[1756518000000, 32.18],
|
||||
[1756604400000, 31.54],
|
||||
[1756690800000, 31.6],
|
||||
[1757036400000, 32.05],
|
||||
[1757122800000, 31.29],
|
||||
[1757209200000, 31.05],
|
||||
[1757295600000, 29.82],
|
||||
[1757554800000, 30.31],
|
||||
[1757641200000, 30.7],
|
||||
[1757727600000, 31.69],
|
||||
[1757814000000, 31.32],
|
||||
[1757900400000, 31.65],
|
||||
[1758159600000, 31.13],
|
||||
[1758246000000, 31.77],
|
||||
[1758332400000, 31.79],
|
||||
[1758418800000, 31.67],
|
||||
[1758505200000, 32.39],
|
||||
[1758764400000, 32.63],
|
||||
[1758850800000, 32.89],
|
||||
[1758937200000, 31.99],
|
||||
[1759023600000, 31.23],
|
||||
[1759110000000, 31.57],
|
||||
[1759369200000, 30.84],
|
||||
[1759455600000, 31.07],
|
||||
[1759542000000, 31.41],
|
||||
[1759628400000, 31.17],
|
||||
[1759714800000, 32.37],
|
||||
[1759974000000, 32.19],
|
||||
[1760060400000, 32.51],
|
||||
[1760233200000, 32.53],
|
||||
[1760319600000, 31.37],
|
||||
[1760578800000, 30.43],
|
||||
[1760665200000, 30.44],
|
||||
[1760751600000, 30.2],
|
||||
[1760838000000, 30.14],
|
||||
[1760924400000, 30.65],
|
||||
[1761183600000, 30.4],
|
||||
[1761270000000, 30.65],
|
||||
[1761356400000, 31.43],
|
||||
[1761442800000, 31.89],
|
||||
[1761529200000, 31.38],
|
||||
[1761788400000, 30.64],
|
||||
[1761874800000, 30.02],
|
||||
[1761961200000, 30.33],
|
||||
[1762047600000, 30.95],
|
||||
[1762134000000, 31.89],
|
||||
[1762393200000, 31.01],
|
||||
[1762479600000, 30.88],
|
||||
[1762566000000, 30.69],
|
||||
[1762652400000, 30.58],
|
||||
[1762738800000, 32.02],
|
||||
[1762998000000, 32.14],
|
||||
[1763084400000, 32.37],
|
||||
[1763170800000, 32.51],
|
||||
[1763257200000, 32.65],
|
||||
[1763343600000, 32.64],
|
||||
[1763602800000, 32.27],
|
||||
[1763689200000, 32.1],
|
||||
[1763775600000, 32.91],
|
||||
[1763862000000, 33.65],
|
||||
[1763948400000, 33.8],
|
||||
[1764207600000, 33.92],
|
||||
[1764294000000, 33.75],
|
||||
[1764380400000, 33.84],
|
||||
[1764466800000, 33.5],
|
||||
[1764553200000, 32.26],
|
||||
[1764812400000, 32.32],
|
||||
[1764898800000, 32.06],
|
||||
[1764985200000, 31.96],
|
||||
[1765071600000, 31.46],
|
||||
[1765158000000, 31.27],
|
||||
[1765503600000, 31.43],
|
||||
[1765590000000, 32.26],
|
||||
[1765676400000, 32.79],
|
||||
[1765762800000, 32.46],
|
||||
[1766022000000, 32.13],
|
||||
[1766108400000, 32.43],
|
||||
[1766194800000, 32.42],
|
||||
[1766281200000, 32.81],
|
||||
[1766367600000, 33.34],
|
||||
[1766626800000, 33.41],
|
||||
[1766713200000, 32.57],
|
||||
[1766799600000, 33.12],
|
||||
[1766886000000, 34.53],
|
||||
[1766972400000, 33.83],
|
||||
[1767231600000, 33.41],
|
||||
[1767318000000, 32.9],
|
||||
[1767404400000, 32.53],
|
||||
[1767490800000, 32.8],
|
||||
[1767577200000, 32.44],
|
||||
[1767836400000, 32.62],
|
||||
[1767922800000, 32.57],
|
||||
[1768009200000, 32.6],
|
||||
[1768095600000, 32.68],
|
||||
[1768182000000, 32.47],
|
||||
[1768441200000, 32.23],
|
||||
[1768527600000, 31.68],
|
||||
[1768614000000, 31.51],
|
||||
[1768700400000, 31.78],
|
||||
[1768786800000, 31.94],
|
||||
[1769046000000, 32.33],
|
||||
[1769132400000, 33.24],
|
||||
[1769218800000, 33.44],
|
||||
[1769305200000, 33.48],
|
||||
[1769391600000, 33.24],
|
||||
[1769650800000, 33.49],
|
||||
[1769737200000, 33.31],
|
||||
[1769823600000, 33.36],
|
||||
[1769910000000, 33.4],
|
||||
[1769996400000, 34.01],
|
||||
[1770432000000, 34.02],
|
||||
[1770518400000, 34.36],
|
||||
[1770604800000, 34.39],
|
||||
[1770864000000, 34.24],
|
||||
[1770950400000, 34.39],
|
||||
[1771036800000, 33.47],
|
||||
[1771123200000, 32.98],
|
||||
[1771209600000, 32.9],
|
||||
[1771468800000, 32.7],
|
||||
[1771555200000, 32.54],
|
||||
[1771641600000, 32.23],
|
||||
[1771728000000, 32.64],
|
||||
[1771814400000, 32.65],
|
||||
[1772073600000, 32.92],
|
||||
[1772160000000, 32.64],
|
||||
[1772246400000, 32.84],
|
||||
[1772419200000, 33.4],
|
||||
[1772678400000, 33.3],
|
||||
[1772764800000, 33.18],
|
||||
[1772851200000, 33.88],
|
||||
[1772937600000, 34.09],
|
||||
[1773024000000, 34.61],
|
||||
[1773283200000, 34.7],
|
||||
[1773369600000, 35.3],
|
||||
[1773456000000, 35.4],
|
||||
[1773542400000, 35.14],
|
||||
[1773628800000, 35.48],
|
||||
[1773888000000, 35.75],
|
||||
[1773974400000, 35.54],
|
||||
[1774060800000, 35.96],
|
||||
[1774147200000, 35.53],
|
||||
[1774233600000, 37.56],
|
||||
[1774492800000, 37.42],
|
||||
[1774579200000, 37.49],
|
||||
[1774665600000, 38.09],
|
||||
[1774752000000, 37.87],
|
||||
];
|
||||
|
||||
const chartOptions = {
|
||||
series: [
|
||||
{
|
||||
name: "Portfolio Performance",
|
||||
data: data,
|
||||
},
|
||||
],
|
||||
legend: {
|
||||
show: false,
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
},
|
||||
colors: ["#465FFF"],
|
||||
chart: {
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
height: 335,
|
||||
id: "area-datetime",
|
||||
type: "area",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
|
||||
stroke: {
|
||||
curve: "straight",
|
||||
width: ["1", "1"],
|
||||
},
|
||||
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
markers: {
|
||||
size: 0,
|
||||
},
|
||||
|
||||
labels: {
|
||||
show: false,
|
||||
position: "top",
|
||||
},
|
||||
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
tickAmount: 10,
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: false,
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
x: {
|
||||
format: "dd MMM yyyy",
|
||||
},
|
||||
},
|
||||
|
||||
fill: {
|
||||
gradient: {
|
||||
enabled: true,
|
||||
opacityFrom: 0.55,
|
||||
opacityTo: 0,
|
||||
},
|
||||
},
|
||||
|
||||
grid: {
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
yaxis: {
|
||||
title: {
|
||||
style: {
|
||||
fontSize: "0px",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const chartThirteen = new ApexCharts(chartThirteenEl, chartOptions);
|
||||
chartThirteen.render();
|
||||
return chartThirteen;
|
||||
}
|
||||
}
|
||||
|
||||
export default initChartThirteen;
|
||||
61
resources/js/components/chart/chart-2.js
Normal file
61
resources/js/components/chart/chart-2.js
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
export const initChartTwo = () => {
|
||||
const chartElement = document.querySelector('#chartTwo');
|
||||
|
||||
if (chartElement) {
|
||||
const chartTwoOptions = {
|
||||
series: [75.55],
|
||||
colors: ["#465FFF"],
|
||||
chart: {
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
type: "radialBar",
|
||||
height: 330,
|
||||
sparkline: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
radialBar: {
|
||||
startAngle: -90,
|
||||
endAngle: 90,
|
||||
hollow: {
|
||||
size: "80%",
|
||||
},
|
||||
track: {
|
||||
background: "#E4E7EC",
|
||||
strokeWidth: "100%",
|
||||
margin: 5, // margin is in pixels
|
||||
},
|
||||
dataLabels: {
|
||||
name: {
|
||||
show: false,
|
||||
},
|
||||
value: {
|
||||
fontSize: "36px",
|
||||
fontWeight: "600",
|
||||
offsetY: 60,
|
||||
color: "#1D2939",
|
||||
formatter: function (val) {
|
||||
return val + "%";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
type: "solid",
|
||||
colors: ["#465FFF"],
|
||||
},
|
||||
stroke: {
|
||||
lineCap: "round",
|
||||
},
|
||||
labels: ["Progress"],
|
||||
};
|
||||
|
||||
const chart = new ApexCharts(chartElement, chartTwoOptions);
|
||||
chart.render();
|
||||
return chart;
|
||||
}
|
||||
}
|
||||
|
||||
export default initChartTwo;
|
||||
107
resources/js/components/chart/chart-3.js
Normal file
107
resources/js/components/chart/chart-3.js
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
export const initChartThree = () => {
|
||||
const chartElement = document.querySelector('#chartThree');
|
||||
|
||||
if (chartElement) {
|
||||
const chartThreeOptions = {
|
||||
series: [{
|
||||
name: "Sales",
|
||||
data: [180, 190, 170, 160, 175, 165, 170, 205, 230, 210, 240, 235],
|
||||
},
|
||||
{
|
||||
name: "Revenue",
|
||||
data: [40, 30, 50, 40, 55, 40, 70, 100, 110, 120, 150, 140],
|
||||
},
|
||||
],
|
||||
legend: {
|
||||
show: false,
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
},
|
||||
colors: ["#465FFF", "#9CB9FF"],
|
||||
chart: {
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
height: 310,
|
||||
type: "area",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
gradient: {
|
||||
enabled: true,
|
||||
opacityFrom: 0.55,
|
||||
opacityTo: 0,
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: "straight",
|
||||
width: ["2", "2"],
|
||||
},
|
||||
markers: {
|
||||
size: 0,
|
||||
},
|
||||
labels: {
|
||||
show: false,
|
||||
position: "top",
|
||||
},
|
||||
grid: {
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: "dd MMM yyyy",
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: "category",
|
||||
categories: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
],
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: false,
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
style: {
|
||||
fontSize: "0px",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const chart = new ApexCharts(chartElement, chartThreeOptions);
|
||||
chart.render();
|
||||
return chart;
|
||||
}
|
||||
}
|
||||
|
||||
export default initChartThree;
|
||||
107
resources/js/components/chart/chart-6.js
Normal file
107
resources/js/components/chart/chart-6.js
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
|
||||
export function initChartSix() {
|
||||
const chartSixEl = document.querySelector('#chartSix');
|
||||
if (chartSixEl) {
|
||||
const chartSixOptions = {
|
||||
series: [
|
||||
{
|
||||
name: "Direct",
|
||||
data: [44, 55, 41, 67, 22, 43, 55, 41],
|
||||
},
|
||||
{
|
||||
name: "Referral",
|
||||
data: [13, 23, 20, 8, 13, 27, 13, 23],
|
||||
},
|
||||
{
|
||||
name: "Organic Search",
|
||||
data: [11, 17, 15, 15, 21, 14, 18, 20],
|
||||
},
|
||||
{
|
||||
name: "Social",
|
||||
data: [21, 7, 25, 13, 22, 8, 18, 20],
|
||||
},
|
||||
],
|
||||
colors: ["#2a31d8", "#465fff", "#7592ff", "#c2d6ff"],
|
||||
chart: {
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
type: "bar",
|
||||
stacked: true,
|
||||
height: 315,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: "39%",
|
||||
borderRadius: 10,
|
||||
borderRadiusApplication: "end",
|
||||
borderRadiusWhenStacked: "last",
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
categories: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"],
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
fontFamily: "Outfit",
|
||||
fontSize: "14px",
|
||||
fontWeight: 400,
|
||||
markers: {
|
||||
size: 5,
|
||||
shape: "circle",
|
||||
radius: 999,
|
||||
strokeWidth: 0,
|
||||
},
|
||||
itemMargin: {
|
||||
horizontal: 10,
|
||||
vertical: 0,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: false,
|
||||
},
|
||||
grid: {
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
x: {
|
||||
show: false,
|
||||
},
|
||||
y: {
|
||||
formatter: function (val) {
|
||||
return val;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const chartSix = new ApexCharts(chartSixEl, chartSixOptions);
|
||||
chartSix.render();
|
||||
return chartSix;
|
||||
}
|
||||
}
|
||||
108
resources/js/components/chart/chart-8.js
Normal file
108
resources/js/components/chart/chart-8.js
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
export function initChartEight() {
|
||||
const chartEightEl = document.querySelector('#chartEight');
|
||||
if (chartEightEl) {
|
||||
const chartEightOptions = {
|
||||
series: [
|
||||
{
|
||||
name: "Sales",
|
||||
data: [180, 190, 170, 160, 175, 165, 170, 205, 230, 210, 240, 235],
|
||||
},
|
||||
{
|
||||
name: "Revenue",
|
||||
data: [40, 30, 50, 40, 55, 40, 70, 100, 110, 120, 150, 140],
|
||||
},
|
||||
],
|
||||
legend: {
|
||||
show: false,
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
},
|
||||
colors: ["#465FFF", "#9CB9FF"],
|
||||
chart: {
|
||||
fontFamily: "Outfit, sans-serif",
|
||||
height: 310,
|
||||
type: "area",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
gradient: {
|
||||
enabled: true,
|
||||
opacityFrom: 0.55,
|
||||
opacityTo: 0,
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
width: ["2", "2"],
|
||||
},
|
||||
|
||||
markers: {
|
||||
size: 0,
|
||||
},
|
||||
labels: {
|
||||
show: false,
|
||||
position: "top",
|
||||
},
|
||||
grid: {
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: "dd MMM yyyy",
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: "category",
|
||||
categories: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
],
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: false,
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
style: {
|
||||
fontSize: "0px",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const chartEight = new ApexCharts(chartEightEl, chartEightOptions);
|
||||
chartEight.render();
|
||||
|
||||
return chartEight;
|
||||
}
|
||||
}
|
||||
export default initChartEight;
|
||||
63
resources/js/components/map.js
Normal file
63
resources/js/components/map.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import jsVectorMap from 'jsvectormap';
|
||||
import 'jsvectormap/dist/maps/world';
|
||||
import 'jsvectormap/dist/jsvectormap.min.css';
|
||||
|
||||
export const initMap = () => {
|
||||
const mapSelectorOne = document.querySelectorAll('#mapOne');
|
||||
|
||||
if (mapSelectorOne.length) {
|
||||
const mapOne = new jsVectorMap({
|
||||
selector: "#mapOne",
|
||||
map: "world",
|
||||
zoomButtons: false,
|
||||
regionStyle: {
|
||||
initial: {
|
||||
fontFamily: "Outfit",
|
||||
fill: "#D9D9D9",
|
||||
},
|
||||
hover: {
|
||||
fillOpacity: 1,
|
||||
fill: "#465fff",
|
||||
},
|
||||
},
|
||||
markers: [
|
||||
{
|
||||
name: "Egypt",
|
||||
coords: [26.8206, 30.8025],
|
||||
},
|
||||
{
|
||||
name: "United Kingdom",
|
||||
coords: [55.3781, 3.436],
|
||||
},
|
||||
{
|
||||
name: "United States",
|
||||
coords: [37.0902, -95.7129],
|
||||
},
|
||||
],
|
||||
|
||||
markerStyle: {
|
||||
initial: {
|
||||
strokeWidth: 1,
|
||||
fill: "#465fff",
|
||||
fillOpacity: 1,
|
||||
r: 4,
|
||||
},
|
||||
hover: {
|
||||
fill: "#465fff",
|
||||
fillOpacity: 1,
|
||||
},
|
||||
selected: {},
|
||||
selectedHover: {},
|
||||
},
|
||||
|
||||
onRegionTooltipShow: function (event, tooltip, code) {
|
||||
tooltip.text(
|
||||
tooltip.text() + (code === "EG" ? " <b>(Hello Russia)</b>" : ""),
|
||||
true // This second parameter enables HTML
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default initMap;
|
||||
161
resources/views/api-keys/index.blade.php
Normal file
161
resources/views/api-keys/index.blade.php
Normal file
@@ -0,0 +1,161 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'API Keys')
|
||||
|
||||
@section('content')
|
||||
<div class="p-6" x-data="apiKeyManager">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">API Keys</h1>
|
||||
<p class="text-gray-500 dark:text-gray-400 mt-1">API keys are used to authenticate requests to the DyDev APP API</p>
|
||||
</div>
|
||||
<button @click="$dispatch('open-modal', 'generate-api-key')"
|
||||
class="px-4 py-2 bg-brand-500 hover:bg-brand-600 text-white rounded-lg transition-colors flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Add API Key</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div x-init="setTimeout(() => $el.remove(), 5000)" class="mb-6 p-4 bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-400 rounded-lg">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('generated_key'))
|
||||
<div class="mb-6 p-6 bg-brand-50 dark:bg-gray-800 border border-brand-200 dark:border-gray-700 rounded-lg">
|
||||
<h3 class="font-bold text-lg text-gray-900 dark:text-white mb-2">New API Key Generated</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-4">Please copy this key immediately. You won't be able to see
|
||||
it again!</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<code
|
||||
class="flex-1 bg-white dark:bg-gray-900 p-3 rounded border border-gray-200 dark:border-gray-700 font-mono text-brand-600 dark:text-brand-400 break-all">
|
||||
{{ session('generated_key') }}
|
||||
</code>
|
||||
<button x-data="{ copied: false }"
|
||||
@click="navigator.clipboard.writeText('{{ session('generated_key') }}'); copied = true; setTimeout(() => copied = false, 2000)"
|
||||
class="px-3 py-3 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
|
||||
<span x-show="!copied">Copy</span>
|
||||
<span x-show="copied" x-cloak class="text-green-600">Copied!</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mb-6 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div class="flex flex-col sm:flex-row items-center gap-4 w-full md:w-auto">
|
||||
<div class="flex items-center gap-2 w-full sm:w-auto">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400 whitespace-nowrap">Show</span>
|
||||
<select name="per_page" x-model="perPage" @change="updateTable()"
|
||||
class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 text-sm rounded-lg focus:ring-brand-500 focus:border-brand-500 block p-2">
|
||||
<option value="5">5</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
</select>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400 whitespace-nowrap">entries</span>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full sm:w-64">
|
||||
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
||||
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="text" x-model="search" @input.debounce.500ms="updateTable()"
|
||||
class="block w-full p-2 pl-10 text-sm text-gray-900 border border-gray-200 rounded-lg bg-white focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:placeholder-gray-400 dark:text-white dark:focus:ring-brand-500 dark:focus:border-brand-500"
|
||||
placeholder="Search API keys...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="api-keys-table-container" class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden relative"
|
||||
@click.prevent="if($event.target.closest('a')) handlePagination($event.target.closest('a').href)">
|
||||
<div x-show="loading" class="absolute inset-0 bg-white/50 dark:bg-gray-800/50 flex items-center justify-center z-10" x-cloak>
|
||||
<svg class="animate-spin h-8 w-8 text-brand-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
@include('api-keys.partials.table')
|
||||
</div>
|
||||
|
||||
@include('api-keys.partials.api-docs')
|
||||
</div>
|
||||
|
||||
@include('api-keys.partials.generate-modal')
|
||||
@include('api-keys.partials.edit-modal')
|
||||
@include('api-keys.partials.delete-modal')
|
||||
@include('api-keys.partials.regenerate-modal')
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data('apiKeyManager', () => ({
|
||||
search: '{{ request('search') }}',
|
||||
perPage: '{{ request('per_page', 10) }}',
|
||||
loading: false,
|
||||
|
||||
async updateTable(url = null) {
|
||||
this.loading = true;
|
||||
if (!url) {
|
||||
url = new URL('{{ route('api-keys.index') }}');
|
||||
url.searchParams.set('search', this.search);
|
||||
url.searchParams.set('per_page', this.perPage);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
const html = await response.text();
|
||||
document.getElementById('api-keys-table-container').innerHTML = html;
|
||||
|
||||
// Re-initialize Alpine components in the new HTML if necessary
|
||||
// Alpine handles this automatically if innerHTML is swapped and elements have x-data
|
||||
} catch (error) {
|
||||
console.error('Error updating table:', error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
handlePagination(url) {
|
||||
if (url && url !== '#') {
|
||||
this.updateTable(url);
|
||||
}
|
||||
},
|
||||
|
||||
maskKey(key) {
|
||||
if (!key) return '';
|
||||
return key.substring(0, 12) + '********************' + key.substring(key.length - 4);
|
||||
},
|
||||
|
||||
async toggleStatus(key) {
|
||||
try {
|
||||
const response = await fetch(`{{ route('api-keys.index') }}/${key.id}/toggle`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
key.is_active = !key.is_active;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error toggling status:', error);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
100
resources/views/api-keys/partials/api-docs.blade.php
Normal file
100
resources/views/api-keys/partials/api-docs.blade.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<div class="mt-8 bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"
|
||||
x-data="{
|
||||
apiMode: 'private',
|
||||
lang: 'curl',
|
||||
baseUrl: window.location.origin,
|
||||
privateEndpoint: '/api/v1/certificates',
|
||||
publicEndpoint: '/api/public/ca-certificates'
|
||||
}">
|
||||
<div class="p-6 border-b border-gray-100 dark:border-gray-700">
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-brand-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||||
</svg>
|
||||
API Documentation
|
||||
</h2>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Learn how to integrate DyDev APP API into your application.</p>
|
||||
</div>
|
||||
|
||||
<!-- API Selection -->
|
||||
<div class="flex p-1 bg-gray-100 dark:bg-gray-900 rounded-lg">
|
||||
<button @click="apiMode = 'private'"
|
||||
:class="apiMode === 'private' ? 'bg-white dark:bg-gray-800 text-brand-600 dark:text-brand-400 shadow-sm' : 'text-gray-500 dark:text-gray-400 hover:text-gray-700'"
|
||||
class="px-4 py-1.5 text-sm font-medium rounded-md transition-all flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
Private API
|
||||
</button>
|
||||
<button @click="apiMode = 'public'"
|
||||
:class="apiMode === 'public' ? 'bg-white dark:bg-gray-800 text-brand-600 dark:text-brand-400 shadow-sm' : 'text-gray-500 dark:text-gray-400 hover:text-gray-700'"
|
||||
class="px-4 py-1.5 text-sm font-medium rounded-md transition-all flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 002 2 2 2 0 012 2v.654M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
Public API
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12">
|
||||
<!-- Sidebar Tabs -->
|
||||
<div class="lg:col-span-3 border-r border-gray-100 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-900/50 p-4">
|
||||
<h3 class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-4 px-2">Language</h3>
|
||||
<div class="space-y-1">
|
||||
<button @click="lang = 'curl'" :class="lang === 'curl' ? 'bg-brand-50 dark:bg-brand-900/20 text-brand-600 dark:text-brand-400' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'" class="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors">
|
||||
<svg viewBox="0 0 24 24" class="h-5 w-5 fill-current" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"/></svg>
|
||||
cURL
|
||||
</button>
|
||||
<button @click="lang = 'js'" :class="lang === 'js' ? 'bg-brand-50 dark:bg-brand-900/20 text-brand-600 dark:text-brand-400' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'" class="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors">
|
||||
<svg viewBox="0 0 24 24" class="h-5 w-5 fill-current" xmlns="http://www.w3.org/2000/svg"><path d="M3 3h18v18H3V3zm14.53 14.12c-.52-.31-.76-.6-.76-1.12 0-.49.33-.82.8-.82.46 0 .76.24.96.59l1.1-.64c-.33-.59-.88-1.07-2.01-1.07-1.11 0-1.99.71-1.99 1.9 0 1.13.68 1.72 1.7 2.3.56.32.78.61.78 1.13 0 .58-.45.92-1.05.92-.72 0-1.15-.36-1.38-.9l-1.14.66c.38.86 1.15 1.34 2.54 1.34 1.48 0 2.21-.83 2.21-1.95 0-1.29-.75-1.93-1.76-2.51zm-5.71 1.63c-.38-.6-.62-.83-.99-.83-.41 0-.61.2-.61.53v4.2c0 .33.22.54.56.54.34 0 .56-.21.56-.54V17.5c.34 0 .53.18.89.73l1.15-.7c-.55-.88-1.04-1.22-2.02-1.22-1.24 0-1.76.85-1.76 1.72 0 .84.44 1.51 1.09 1.94-.58.39-.77.92-.77 1.63 0 1.05.73 1.71 1.75 1.71.97 0 1.57-.42 2.05-1.2l-1.12-.66c-.34.56-.59.78-1 .78-.44 0-.68-.26-.68-.61v-2.29z"/></svg>
|
||||
JavaScript
|
||||
</button>
|
||||
<button @click="lang = 'php'" :class="lang === 'php' ? 'bg-brand-50 dark:bg-brand-900/20 text-brand-600 dark:text-brand-400' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'" class="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors">
|
||||
<svg viewBox="0 0 24 24" class="h-5 w-5 fill-current" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm6.36 14.83c-.08.23-.29.33-.6.33-.31 0-.58-.1-.7-.34l-.87-1.84h-2.12l-.46 1.83c-.08.31-.3.43-.63.43-.32 0-.54-.12-.54-.42 0-.04.01-.09.02-.15l1.65-6.19c.12-.46.39-.68.86-.68.46 0 .71.21.82.63l2.6 6.42zm-5-3.35h1.56l-.78-1.63-.78 1.63zM8.5 15.17l.02.16c0 .3-.22.42-.54.42-.33 0-.55-.12-.63-.43L5.7 8.16C5.69 8.1 5.68 8.05 5.68 8c0-.3.22-.42.54-.42.33 0 .55.12.63.43l1.65 6.16z"/></svg>
|
||||
PHP
|
||||
</button>
|
||||
<button @click="lang = 'python'" :class="lang === 'python' ? 'bg-brand-50 dark:bg-brand-900/20 text-brand-600 dark:text-brand-400' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'" class="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors">
|
||||
<svg viewBox="0 0 24 24" class="h-5 w-5 fill-current" xmlns="http://www.w3.org/2000/svg"><path d="M11.97 2c-2.47 0-2.31 1.07-2.31 1.07l.01 1.11h2.36v.34H8.64s-2.15-.26-2.15 2.1c0 2.37 1.86 2.22 1.86 2.22h1.11v-1.55c0 0-.08-1.86 1.86-1.86h3.1s1.84 0 1.84-1.78V5.21s.11-1.79-1.84-1.79c-1.95-.01-2.45-.42-2.45-.42S13.44 2 11.97 2zM7.46 10.45s-1.84 0-1.84 1.78v1.44s-.11 1.79 1.84 1.79c1.95 0 2.45.42 2.45.42s.53 1.02 2 1.02c2.47 0 2.31-1.07 2.31-1.07l-.01-1.11H12.1v-.34h3.64s2.15.26 2.15-2.1c0-2.37-1.86-2.22-1.86-2.22H7.46zm1.39 1.11c.31 0 .56.25.56.56s-.25.56-.56.56-.56-.25-.56-.56.25-.56.56-.56zm3.33 7.22c-.31 0-.56-.25-.56-.56s.25-.56.56-.56.56.25.56.56-.25.56-.56.56z"/></svg>
|
||||
Python
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Code Content -->
|
||||
<div class="lg:col-span-9 bg-gray-50 dark:bg-gray-900/30">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-sm font-medium text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 text-[10px] font-bold uppercase" x-text="apiMode === 'private' ? 'Authenticated' : 'Public'"></span>
|
||||
<code class="text-brand-600 dark:text-brand-400" x-text="apiMode === 'private' ? privateEndpoint : publicEndpoint"></code>
|
||||
</h4>
|
||||
<button @click="navigator.clipboard.writeText($refs.codeContent.innerText)" class="p-1 px-2 text-[10px] font-bold text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 uppercase flex items-center gap-1 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Copy Snippet
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative group">
|
||||
<pre class="rounded-xl bg-white dark:bg-gray-950 p-6 overflow-x-auto border border-gray-200 dark:border-gray-800 shadow-inner"><code class="text-sm font-mono leading-relaxed" x-ref="codeContent"><template x-if="lang === 'curl'"><span class="text-gray-800 dark:text-gray-300" x-text="'curl -X GET ' + baseUrl + (apiMode === 'private' ? privateEndpoint : publicEndpoint) + '\n -H \'Accept: application/json\'' + (apiMode === 'private' ? '\n -H \'X-API-KEY: your_api_key_here\'' : '')"></span></template><template x-if="lang === 'js'"><span class="text-gray-800 dark:text-gray-300" x-text="'fetch(\'' + baseUrl + (apiMode === 'private' ? privateEndpoint : publicEndpoint) + '\', {\n method: \'GET\', \n headers: {\n \'Accept\': \'application/json\'' + (apiMode === 'private' ? ',\n \'X-API-KEY\': \'your_api_key_here\'' : '') + '\n }\n})\n.then(response => response.json())\n.then(data => console.log(data));'"></span></template><template x-if="lang === 'php'"><span class="text-gray-800 dark:text-gray-300" x-text="'$ch = curl_init();\ncurl_setopt($ch, CURLOPT_URL, \'' + baseUrl + (apiMode === 'private' ? privateEndpoint : publicEndpoint) + '\');\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\n \'Accept: application/json\'' + (apiMode === 'private' ? ',\n \'X-API-KEY: your_api_key_here\'' : '') + '\n]);\n$response = curl_exec($ch);\ncurl_close($ch);\necho $response;'"></span></template><template x-if="lang === 'python'"><span class="text-gray-800 dark:text-gray-300" x-text="'import requests\n\nurl = \'' + baseUrl + (apiMode === 'private' ? privateEndpoint : publicEndpoint) + '\'\nheaders = {\n \'Accept\': \'application/json\'' + (apiMode === 'private' ? ',\n \'X-API-KEY\': \'your_api_key_here\'' : '') + '\n}\n\nresponse = requests.get(url, headers=headers)\nprint(response.json())'"></span></template></code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 p-4 rounded-lg bg-orange-50 dark:bg-orange-900/10 border border-orange-100 dark:border-orange-900/20" x-show="apiMode === 'private'">
|
||||
<div class="flex gap-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-orange-600 dark:text-orange-400 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<p class="text-xs text-orange-800 dark:text-orange-300 leading-normal">
|
||||
<strong>Security Note:</strong> Your API Key should never be shared or exposed in client-side code (browsers). Always use environment variables and call these endpoints from your backend servers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
43
resources/views/api-keys/partials/delete-modal.blade.php
Normal file
43
resources/views/api-keys/partials/delete-modal.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<div x-data="{ selectedKey: null }">
|
||||
<x-ui.modal
|
||||
x-on:open-modal.window="if ($event.detail.name === 'delete-api-key') { open = true; selectedKey = $event.detail.apiKey; }"
|
||||
:isOpen="false"
|
||||
containerClass="max-w-[700px]">
|
||||
<div class="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Delete API Key
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
Are you sure you want to delete this API key? This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="px-2 text-center mb-8">
|
||||
<div class="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-red-100 dark:bg-red-900/10">
|
||||
<svg class="h-10 w-10 text-red-600 dark:text-red-500" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="mt-4 text-base text-gray-600 dark:text-gray-400">
|
||||
Any applications using the key <span x-text="selectedKey ? selectedKey.name : ''" class="font-bold text-gray-900 dark:text-white"></span> will stop working immediately.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form x-bind:action="selectedKey ? '{{ route('api-keys.index') }}/' + selectedKey.id : '#'" method="POST" class="flex flex-col">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<div class="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button"
|
||||
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="flex w-full justify-center rounded-lg bg-red-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-red-700 sm:w-auto">
|
||||
Delete API Key
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
</div>
|
||||
71
resources/views/api-keys/partials/edit-modal.blade.php
Normal file
71
resources/views/api-keys/partials/edit-modal.blade.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<div x-data="{ selectedKey: null }">
|
||||
<x-ui.modal
|
||||
x-on:open-modal.window="if ($event.detail.name === 'edit-api-key') { open = true; selectedKey = $event.detail.apiKey; }"
|
||||
:isOpen="false"
|
||||
containerClass="max-w-[700px]">
|
||||
<div class="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Edit API Key
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
Update the name of your API key to keep your applications organized.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form x-bind:action="selectedKey ? '{{ route('api-keys.index') }}/' + selectedKey.id : '#'" method="POST"
|
||||
@submit.prevent="submitEditForm" class="flex flex-col">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="px-2">
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<label for="edit_name" class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Application Name
|
||||
</label>
|
||||
<input type="text" name="name" id="edit_name" required x-model="selectedKey ? selectedKey.name : ''"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button"
|
||||
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="flex w-full justify-center rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function submitEditForm(e) {
|
||||
const form = e.target;
|
||||
const action = form.getAttribute('action');
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch(action, {
|
||||
method: 'POST', // Method spoofing is handled by _method field
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Error updating API key');
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
</script>
|
||||
</x-ui.modal>
|
||||
</div>
|
||||
42
resources/views/api-keys/partials/generate-modal.blade.php
Normal file
42
resources/views/api-keys/partials/generate-modal.blade.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<x-ui.modal @open-modal.window="if ($event.detail === 'generate-api-key') open = true" :isOpen="false" containerClass="max-w-[700px]">
|
||||
<div class="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Generate API Key
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
To enable secure access to the web services, your app requires an API key with permissions for resources.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('api-keys.store') }}" method="POST" class="flex flex-col">
|
||||
@csrf
|
||||
<div class="px-2">
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<label for="name" class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Application Name
|
||||
</label>
|
||||
<input type="text" name="name" id="name" required
|
||||
placeholder="e.g. My Awesome App"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
<p class="mt-2 text-xs text-gray-500 dark:text-gray-500">
|
||||
Naming your application makes it easier to recognize your API key in the future.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button"
|
||||
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="flex w-full justify-center rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto">
|
||||
Generate API Key
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
123
resources/views/api-keys/partials/regenerate-modal.blade.php
Normal file
123
resources/views/api-keys/partials/regenerate-modal.blade.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<div x-data="{
|
||||
show: false,
|
||||
selectedKey: null,
|
||||
newKey: '',
|
||||
step: 'confirm',
|
||||
loading: false,
|
||||
async regenerate() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(`{{ route('api-keys.index') }}/${this.selectedKey.id}/regenerate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
this.newKey = data.new_key;
|
||||
this.step = 'success';
|
||||
// Update the key in the table if possible
|
||||
if (this.selectedKey) {
|
||||
this.selectedKey.key = data.new_key;
|
||||
}
|
||||
} else {
|
||||
alert(data.message || 'Error regenerating API key');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error regenerating API key. Please try again.');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}"
|
||||
x-on:open-modal.window="if ($event.detail.name === 'confirm-regenerate-api-key') { show = true; selectedKey = $event.detail.apiKey; step = 'confirm'; newKey = ''; loading = false; }">
|
||||
<x-ui.modal x-model="show" :isOpen="false" containerClass="max-w-[700px]">
|
||||
|
||||
<div class="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
||||
|
||||
<!-- Step: Confirm -->
|
||||
<template x-if="step === 'confirm'">
|
||||
<div>
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Regenerate API Key
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
Are you sure you want to regenerate this API key?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="px-2 text-center mb-8">
|
||||
<div class="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-brand-100 dark:bg-brand-900/10">
|
||||
<svg class="h-10 w-10 text-brand-600 dark:text-brand-500" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="mt-4 text-base text-gray-600 dark:text-gray-400">
|
||||
The old key for <span x-text="selectedKey ? selectedKey.name : ''" class="font-bold text-gray-900 dark:text-white"></span> will stop working immediately.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button" :disabled="loading"
|
||||
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto disabled:opacity-50">
|
||||
Cancel
|
||||
</button>
|
||||
<button @click="regenerate()" type="button" :disabled="loading"
|
||||
class="flex w-full items-center justify-center gap-2 rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto disabled:opacity-50">
|
||||
<svg x-show="loading" class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span x-text="loading ? 'Regenerating...' : 'Regenerate Key'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Step: Success -->
|
||||
<template x-if="step === 'success'">
|
||||
<div>
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
New API Key Generated
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
Please copy your new API key now. You won't be able to see it again!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="px-2 mb-8">
|
||||
<div class="flex items-center gap-2">
|
||||
<code x-text="newKey"
|
||||
class="flex-1 bg-gray-50 dark:bg-gray-800 p-4 rounded-xl border border-gray-200 dark:border-gray-700 font-mono text-brand-600 dark:text-brand-400 break-all text-sm">
|
||||
</code>
|
||||
<button x-data="{ copied: false }"
|
||||
@click="navigator.clipboard.writeText(newKey); copied = true; setTimeout(() => copied = false, 2000)"
|
||||
class="p-4 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-xl hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors border border-gray-200 dark:border-gray-700">
|
||||
<svg x-show="!copied" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<svg x-show="copied" x-cloak xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button"
|
||||
class="flex w-full justify-center rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto">
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
</div>
|
||||
|
||||
98
resources/views/api-keys/partials/table.blade.php
Normal file
98
resources/views/api-keys/partials/table.blade.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-200 dark:border-gray-700 text-xs text-gray-500 dark:text-gray-400 uppercase">
|
||||
<th class="px-6 py-4 font-medium">Name</th>
|
||||
<th class="px-6 py-4 font-medium">Status</th>
|
||||
<th class="px-6 py-4 font-medium">Created</th>
|
||||
<th class="px-6 py-4 font-medium">Last used</th>
|
||||
<th class="px-6 py-4 font-medium">Disable/Enable</th>
|
||||
<th class="px-6 py-4 font-medium">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@forelse($apiKeys as $key)
|
||||
<tr class="bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors" x-data="{ editingKey: { ...@js($key), regenerating: false } }">
|
||||
<td class="px-6 py-4">
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white" x-text="editingKey.name"></div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="relative flex-1 max-w-xs">
|
||||
<input type="text" readonly :value="maskKey(editingKey.key)"
|
||||
class="w-full bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg py-1.5 pl-3 pr-10 text-xs font-mono text-gray-600 dark:text-gray-400 focus:outline-none focus:ring-0">
|
||||
</div>
|
||||
<button x-data="{ copied: false }"
|
||||
@click="navigator.clipboard.writeText(editingKey.key); copied = true; setTimeout(() => copied = false, 2000)"
|
||||
class="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 border border-gray-200 dark:border-gray-700 rounded-lg transition-colors">
|
||||
<svg x-show="!copied" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<svg x-show="copied" x-cloak xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-green-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<button @click="$dispatch('open-modal', { name: 'confirm-regenerate-api-key', apiKey: editingKey })"
|
||||
class="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 border border-gray-200 dark:border-gray-700 rounded-lg transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
|
||||
:class="editingKey.is_active ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-500' : 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-500'"
|
||||
x-text="editingKey.is_active ? 'Active' : 'Disabled'">
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $key->created_at->format('M d, Y') }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $key->last_used_at ? $key->last_used_at->format('m/d/Y, h:i:s A') : 'Never' }}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<button @click="toggleStatus(editingKey)" class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"
|
||||
:class="editingKey.is_active ? 'bg-brand-500' : 'bg-gray-200 dark:bg-gray-700'" role="switch" :aria-checked="editingKey.is_active">
|
||||
<span aria-hidden="true" class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
|
||||
:class="editingKey.is_active ? 'translate-x-5' : 'translate-x-0'"></span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<button @click="$dispatch('open-modal', { name: 'delete-api-key', apiKey: editingKey })"
|
||||
class="text-gray-400 hover:text-red-500 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
<button @click="$dispatch('open-modal', { name: 'edit-api-key', apiKey: editingKey })"
|
||||
class="text-gray-400 hover:text-brand-500 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-12 text-center text-gray-500 dark:text-gray-400">
|
||||
@if(request('search'))
|
||||
No API keys found matching "{{ request('search') }}".
|
||||
@else
|
||||
No API keys found. Generate one to get started.
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($apiKeys->hasPages())
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-900/50">
|
||||
{{ $apiKeys->links() }}
|
||||
</div>
|
||||
@endif
|
||||
159
resources/views/components/calender-area.blade.php
Normal file
159
resources/views/components/calender-area.blade.php
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
<div>
|
||||
<div class="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div class="custom-calendar">
|
||||
<div id="calendar" class="min-h-screen"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="fixed inset-0 items-center justify-center hidden p-5 overflow-y-auto modal z-99999" id="eventModal">
|
||||
<div class="modal-close-btn fixed inset-0 h-full w-full bg-gray-400/50 backdrop-blur-[32px]"></div>
|
||||
<div class="modal-dialog relative flex w-full max-w-[700px] flex-col overflow-y-auto rounded-3xl bg-white p-6 lg:p-11 dark:bg-gray-900">
|
||||
|
||||
<!-- Close Button -->
|
||||
<button class="modal-close-btn transition-color absolute top-5 right-5 z-999 flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 text-gray-400 hover:bg-gray-200 hover:text-gray-600 sm:h-11 sm:w-11 dark:bg-white/[0.05] dark:text-gray-400 dark:hover:bg-white/[0.07] dark:hover:text-gray-300">
|
||||
<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.04289 16.5418C5.65237 16.9323 5.65237 17.5655 6.04289 17.956C6.43342 18.3465 7.06658 18.3465 7.45711 17.956L11.9987 13.4144L16.5408 17.9565C16.9313 18.347 17.5645 18.347 17.955 17.9565C18.3455 17.566 18.3455 16.9328 17.955 16.5423L13.4129 12.0002L17.955 7.45808C18.3455 7.06756 18.3455 6.43439 17.955 6.04387C17.5645 5.65335 16.9313 5.65335 16.5408 6.04387L11.9987 10.586L7.45711 6.04439C7.06658 5.65386 6.43342 5.65386 6.04289 6.04439C5.65237 6.43491 5.65237 7.06808 6.04289 7.4586L10.5845 12.0002L6.04289 16.5418Z" fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="flex flex-col px-2 overflow-y-auto modal-content custom-scrollbar">
|
||||
|
||||
<!-- Modal Header -->
|
||||
<div class="modal-header">
|
||||
<h5 class="mb-2 font-semibold text-gray-800 modal-title text-theme-xl lg:text-2xl dark:text-white/90" id="eventModalLabel">
|
||||
Add Event
|
||||
</h5>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Plan your next big moment: schedule or edit an event to stay on track
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Modal Body -->
|
||||
<div class="mt-8 modal-body">
|
||||
|
||||
<!-- Event Title -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Event Title
|
||||
</label>
|
||||
<input id="event-title" type="text" class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" placeholder="Enter event title" />
|
||||
</div>
|
||||
|
||||
<!-- Event Color -->
|
||||
<div class="mt-6">
|
||||
<label class="block mb-4 text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Event Color
|
||||
</label>
|
||||
<div class="flex flex-wrap items-center gap-4 sm:gap-5">
|
||||
|
||||
<!-- Danger -->
|
||||
<div class="n-chk">
|
||||
<div class="form-check form-check-danger form-check-inline">
|
||||
<label class="flex items-center text-sm text-gray-700 form-check-label dark:text-gray-400" for="modalDanger">
|
||||
<span class="relative">
|
||||
<input class="sr-only form-check-input" type="radio" name="event-level" value="Danger" id="modalDanger" />
|
||||
<span class="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
||||
</span>
|
||||
</span>
|
||||
Danger
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Success -->
|
||||
<div class="n-chk">
|
||||
<div class="form-check form-check-success form-check-inline">
|
||||
<label class="flex items-center text-sm text-gray-700 form-check-label dark:text-gray-400" for="modalSuccess">
|
||||
<span class="relative">
|
||||
<input class="sr-only form-check-input" type="radio" name="event-level" value="Success" id="modalSuccess" />
|
||||
<span class="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
||||
</span>
|
||||
</span>
|
||||
Success
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary -->
|
||||
<div class="n-chk">
|
||||
<div class="form-check form-check-primary form-check-inline">
|
||||
<label class="flex items-center text-sm text-gray-700 form-check-label dark:text-gray-400" for="modalPrimary">
|
||||
<span class="relative">
|
||||
<input class="sr-only form-check-input" type="radio" name="event-level" value="Primary" id="modalPrimary" />
|
||||
<span class="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
||||
</span>
|
||||
</span>
|
||||
Primary
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warning -->
|
||||
<div class="n-chk">
|
||||
<div class="form-check form-check-warning form-check-inline">
|
||||
<label class="flex items-center text-sm text-gray-700 form-check-label dark:text-gray-400" for="modalWarning">
|
||||
<span class="relative">
|
||||
<input class="sr-only form-check-input" type="radio" name="event-level" value="Warning" id="modalWarning" />
|
||||
<span class="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
||||
</span>
|
||||
</span>
|
||||
Warning
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Start Date -->
|
||||
<div class="mt-6">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Enter Start Date
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input id="event-start-date" type="date" class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" onclick="this.showPicker()" />
|
||||
<span class="absolute top-1/2 right-3.5 -translate-y-1/2 pointer-events-none">
|
||||
<svg class="fill-gray-700 dark:fill-gray-400" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.33317 0.0830078C4.74738 0.0830078 5.08317 0.418794 5.08317 0.833008V1.24967H8.9165V0.833008C8.9165 0.418794 9.25229 0.0830078 9.6665 0.0830078C10.0807 0.0830078 10.4165 0.418794 10.4165 0.833008V1.24967L11.3332 1.24967C12.2997 1.24967 13.0832 2.03318 13.0832 2.99967V4.99967V11.6663C13.0832 12.6328 12.2997 13.4163 11.3332 13.4163H2.6665C1.70001 13.4163 0.916504 12.6328 0.916504 11.6663V4.99967V2.99967C0.916504 2.03318 1.70001 1.24967 2.6665 1.24967L3.58317 1.24967V0.833008C3.58317 0.418794 3.91896 0.0830078 4.33317 0.0830078ZM4.33317 2.74967H2.6665C2.52843 2.74967 2.4165 2.8616 2.4165 2.99967V4.24967H11.5832V2.99967C11.5832 2.8616 11.4712 2.74967 11.3332 2.74967H9.6665H4.33317ZM11.5832 5.74967H2.4165V11.6663C2.4165 11.8044 2.52843 11.9163 2.6665 11.9163H11.3332C11.4712 11.9163 11.5832 11.8044 11.5832 11.6663V5.74967Z" fill="" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End Date -->
|
||||
<div class="mt-6">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Enter End Date
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input id="event-end-date" type="date" class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" onclick="this.showPicker()" />
|
||||
<span class="absolute top-1/2 right-3.5 -translate-y-1/2 pointer-events-none">
|
||||
<svg class="fill-gray-700 dark:fill-gray-400" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.33317 0.0830078C4.74738 0.0830078 5.08317 0.418794 5.08317 0.833008V1.24967H8.9165V0.833008C8.9165 0.418794 9.25229 0.0830078 9.6665 0.0830078C10.0807 0.0830078 10.4165 0.418794 10.4165 0.833008V1.24967L11.3332 1.24967C12.2997 1.24967 13.0832 2.03318 13.0832 2.99967V4.99967V11.6663C13.0832 12.6328 12.2997 13.4163 11.3332 13.4163H2.6665C1.70001 13.4163 0.916504 12.6328 0.916504 11.6663V4.99967V2.99967C0.916504 2.03318 1.70001 1.24967 2.6665 1.24967L3.58317 1.24967V0.833008C3.58317 0.418794 3.91896 0.0830078 4.33317 0.0830078ZM4.33317 2.74967H2.6665C2.52843 2.74967 2.4165 2.8616 2.4165 2.99967V4.24967H11.5832V2.99967C11.5832 2.8616 11.4712 2.74967 11.3332 2.74967H9.6665H4.33317ZM11.5832 5.74967H2.4165V11.6663C2.4165 11.8044 2.52843 11.9163 2.6665 11.9163H11.3332C11.4712 11.9163 11.5832 11.8044 11.5832 11.6663V5.74967Z" fill="" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Modal Footer -->
|
||||
<div class="flex items-center gap-3 mt-6 modal-footer sm:justify-end">
|
||||
<button type="button" class="modal-close-btn flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 sm:w-auto dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03]">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-update-event bg-brand-500 hover:bg-brand-600 flex w-full justify-center rounded-lg px-4 py-2.5 text-sm font-medium text-white sm:w-auto" style="display: none;" data-fc-event-public-id="">
|
||||
Update Changes
|
||||
</button>
|
||||
<button type="button" class="btn btn-add-event bg-brand-500 hover:bg-brand-600 flex w-full justify-center rounded-lg px-4 py-2.5 text-sm font-medium text-white sm:w-auto">
|
||||
Add Event
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<div>
|
||||
<div class="absolute right-0 top-0 -z-1 w-full max-w-[250px] xl:max-w-[450px]">
|
||||
<img src="/images/shape/grid-01.svg" alt="grid" />
|
||||
</div>
|
||||
<div class="absolute bottom-0 left-0 -z-1 w-full max-w-[250px] rotate-180 xl:max-w-[450px]">
|
||||
<img src="/images/shape/grid-01.svg" alt="grid" />
|
||||
</div>
|
||||
</div>
|
||||
29
resources/views/components/common/component-card.blade.php
Normal file
29
resources/views/components/common/component-card.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
@props([
|
||||
'title',
|
||||
'desc' => '',
|
||||
])
|
||||
|
||||
<div {{ $attributes->merge(['class' => 'rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]']) }}>
|
||||
<!-- Card Header -->
|
||||
<div class="px-6 py-5">
|
||||
@isset($header)
|
||||
{{ $header }}
|
||||
@else
|
||||
<h3 class="text-base font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $title }}
|
||||
</h3>
|
||||
@if($desc)
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $desc }}
|
||||
</p>
|
||||
@endif
|
||||
@endisset
|
||||
</div>
|
||||
|
||||
<!-- Card Body -->
|
||||
<div class="p-4 border-t border-gray-100 dark:border-gray-800 sm:p-6">
|
||||
<div class="space-y-6">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
22
resources/views/components/common/dropdown-menu.blade.php
Normal file
22
resources/views/components/common/dropdown-menu.blade.php
Normal file
@@ -0,0 +1,22 @@
|
||||
@props(['items' => ['View More','Delete']])
|
||||
<div x-data="{openDropDown: false}" class="relative h-fit">
|
||||
<button
|
||||
@click="openDropDown = !openDropDown"
|
||||
:class="openDropDown ? 'text-gray-700 dark:text-white' : 'text-gray-400 hover:text-gray-700 dark:hover:text-white'"
|
||||
>
|
||||
<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2441 6C10.2441 5.0335 11.0276 4.25 11.9941 4.25H12.0041C12.9706 4.25 13.7541 5.0335 13.7541 6C13.7541 6.9665 12.9706 7.75 12.0041 7.75H11.9941C11.0276 7.75 10.2441 6.9665 10.2441 6ZM10.2441 18C10.2441 17.0335 11.0276 16.25 11.9941 16.25H12.0041C12.9706 16.25 13.7541 17.0335 13.7541 18C13.7541 18.9665 12.9706 19.75 12.0041 19.75H11.9941C11.0276 19.75 10.2441 18.9665 10.2441 18ZM11.9941 10.25C11.0276 10.25 10.2441 11.0335 10.2441 12C10.2441 12.9665 11.0276 13.75 11.9941 13.75H12.0041C12.9706 13.75 13.7541 12.9665 13.7541 12C13.7541 11.0335 12.9706 10.25 12.0041 10.25H11.9941Z" fill=""/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div x-show="openDropDown" @click.outside="openDropDown = false"
|
||||
class="absolute right-0 z-40 w-40 p-2 space-y-1 bg-white border border-gray-200 shadow-theme-lg dark:bg-gray-dark top-full rounded-2xl dark:border-gray-800">
|
||||
@forelse($items as $item)
|
||||
<button class="flex w-full px-3 py-2 font-medium text-left text-gray-500 rounded-lg text-theme-xs hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300">
|
||||
{{ $item }}
|
||||
</button>
|
||||
@empty
|
||||
{{ $slot }}
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
38
resources/views/components/common/page-breadcrumb.blade.php
Normal file
38
resources/views/components/common/page-breadcrumb.blade.php
Normal file
@@ -0,0 +1,38 @@
|
||||
@props(['pageTitle' => 'Page'])
|
||||
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 mb-6">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-white/90">
|
||||
{{ $pageTitle }}
|
||||
</h2>
|
||||
<nav>
|
||||
<ol class="flex items-center gap-1.5">
|
||||
<li>
|
||||
<a
|
||||
class="inline-flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400"
|
||||
href="{{ url('/') }}"
|
||||
>
|
||||
Home
|
||||
<svg
|
||||
class="stroke-current"
|
||||
width="17"
|
||||
height="16"
|
||||
viewBox="0 0 17 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.0765 12.667L10.2432 8.50033L6.0765 4.33366"
|
||||
stroke=""
|
||||
stroke-width="1.2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="text-sm text-gray-800 dark:text-white/90">
|
||||
{{ $pageTitle }}
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
9
resources/views/components/common/preloader.blade.php
Normal file
9
resources/views/components/common/preloader.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<div
|
||||
x-show="loaded"
|
||||
x-init="window.addEventListener('DOMContentLoaded', () => {setTimeout(() => loaded = false, 350)})"
|
||||
class="fixed left-0 top-0 z-999999 flex h-screen w-screen items-center justify-center bg-white dark:bg-black"
|
||||
>
|
||||
<div
|
||||
class="h-16 w-16 animate-spin rounded-full border-4 border-solid border-brand-500 border-t-transparent"
|
||||
></div>
|
||||
</div>
|
||||
39
resources/views/components/common/table-dropdown.blade.php
Normal file
39
resources/views/components/common/table-dropdown.blade.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<div x-data="{
|
||||
isOpen: false,
|
||||
popperInstance: null,
|
||||
init() {
|
||||
this.$nextTick(() => {
|
||||
this.popperInstance = createPopper(this.$refs.button, this.$refs.content, {
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 4],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
},
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
if (this.popperInstance) {
|
||||
this.popperInstance.update();
|
||||
}
|
||||
}
|
||||
}"
|
||||
@click.away="isOpen = false">
|
||||
<div @click="toggle()" x-ref="button" class="cursor-pointer">
|
||||
{{ $button }}
|
||||
</div>
|
||||
|
||||
<div class="z-50 fixed" x-ref="content">
|
||||
<div x-show="isOpen" x-cloak class="p-2 bg-white border border-gray-200 rounded-2xl shadow-lg dark:border-gray-800 dark:bg-gray-dark w-40">
|
||||
<div class="space-y-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
|
||||
{{ $content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
30
resources/views/components/common/theme-toggle.blade.php
Normal file
30
resources/views/components/common/theme-toggle.blade.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<button
|
||||
x-data="{ theme: localStorage.getItem('theme') || 'light' }"
|
||||
x-init="
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
"
|
||||
@click="
|
||||
theme = theme === 'light' ? 'dark' : 'light';
|
||||
localStorage.setItem('theme', theme);
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
"
|
||||
class="relative flex items-center justify-center text-gray-500 transition-colors bg-white border border-gray-200 rounded-full hover:text-dark-900 h-11 w-11 hover:bg-gray-100 hover:text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
|
||||
>
|
||||
<!-- Dark Icon -->
|
||||
<svg x-show="theme === 'dark'" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20" width="20" height="20">
|
||||
<path fill="currentColor" d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415Z" />
|
||||
<!-- (rest of moon icon path here) -->
|
||||
</svg>
|
||||
|
||||
<!-- Light Icon -->
|
||||
<svg x-show="theme === 'light'" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20" width="20" height="20">
|
||||
<path fill="currentColor" d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97Z" />
|
||||
<!-- (rest of sun icon path here) -->
|
||||
</svg>
|
||||
</button>
|
||||
@@ -0,0 +1,73 @@
|
||||
@props(['countries' => []])
|
||||
|
||||
@php
|
||||
$defaultCountries = [
|
||||
[
|
||||
'name' => 'USA',
|
||||
'flag' => './images/country/country-01.svg',
|
||||
'customers' => '2,379',
|
||||
'percentage' => 79
|
||||
],
|
||||
[
|
||||
'name' => 'France',
|
||||
'flag' => './images/country/country-02.svg',
|
||||
'customers' => '589',
|
||||
'percentage' => 23
|
||||
],
|
||||
];
|
||||
|
||||
$countriesList = !empty($countries) ? $countries : $defaultCountries;
|
||||
@endphp
|
||||
|
||||
<div class="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] sm:p-6">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Customers Demographic
|
||||
</h3>
|
||||
<p class="mt-1 text-theme-sm text-gray-500 dark:text-gray-400">
|
||||
Number of customer based on country
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Menu -->
|
||||
<x-common.dropdown-menu />
|
||||
<!-- End Dropdown Menu -->
|
||||
</div>
|
||||
|
||||
<div class="border-gary-200 my-6 overflow-hidden rounded-2xl border bg-gray-50 px-4 py-6 dark:border-gray-800 dark:bg-gray-900 sm:px-6">
|
||||
<div id="mapOne" class="mapOne map-btn -mx-4 -my-6 h-[212px] w-[252px] 2xsm:w-[307px] xsm:w-[358px] sm:-mx-6 md:w-[668px] lg:w-[634px] xl:w-[393px] 2xl:w-[554px]"></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-5">
|
||||
@foreach($countriesList as $country)
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-full max-w-8 items-center rounded-full">
|
||||
<img src="{{ $country['flag'] }}" alt="{{ strtolower($country['name']) }}" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-theme-sm font-semibold text-gray-800 dark:text-white/90">
|
||||
{{ $country['name'] }}
|
||||
</p>
|
||||
<span class="block text-theme-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $country['customers'] }} Customers
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full max-w-[140px] items-center gap-3">
|
||||
<div class="relative block h-2 w-full max-w-[100px] rounded-sm bg-gray-200 dark:bg-gray-800">
|
||||
<div
|
||||
class="absolute left-0 top-0 flex h-full items-center justify-center rounded-sm bg-brand-500 text-xs font-medium text-white"
|
||||
style="width: {{ $country['percentage'] }}%"
|
||||
></div>
|
||||
</div>
|
||||
<p class="text-theme-sm font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $country['percentage'] }}%
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
107
resources/views/components/ecommerce/ecommerce-metrics.blade.php
Normal file
107
resources/views/components/ecommerce/ecommerce-metrics.blade.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-6">
|
||||
<div
|
||||
class="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800"
|
||||
>
|
||||
<svg
|
||||
class="fill-gray-800 dark:fill-white/90"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.80443 5.60156C7.59109 5.60156 6.60749 6.58517 6.60749 7.79851C6.60749 9.01185 7.59109 9.99545 8.80443 9.99545C10.0178 9.99545 11.0014 9.01185 11.0014 7.79851C11.0014 6.58517 10.0178 5.60156 8.80443 5.60156ZM5.10749 7.79851C5.10749 5.75674 6.76267 4.10156 8.80443 4.10156C10.8462 4.10156 12.5014 5.75674 12.5014 7.79851C12.5014 9.84027 10.8462 11.4955 8.80443 11.4955C6.76267 11.4955 5.10749 9.84027 5.10749 7.79851ZM4.86252 15.3208C4.08769 16.0881 3.70377 17.0608 3.51705 17.8611C3.48384 18.0034 3.5211 18.1175 3.60712 18.2112C3.70161 18.3141 3.86659 18.3987 4.07591 18.3987H13.4249C13.6343 18.3987 13.7992 18.3141 13.8937 18.2112C13.9797 18.1175 14.017 18.0034 13.9838 17.8611C13.7971 17.0608 13.4132 16.0881 12.6383 15.3208C11.8821 14.572 10.6899 13.955 8.75042 13.955C6.81096 13.955 5.61877 14.572 4.86252 15.3208ZM3.8071 14.2549C4.87163 13.2009 6.45602 12.455 8.75042 12.455C11.0448 12.455 12.6292 13.2009 13.6937 14.2549C14.7397 15.2906 15.2207 16.5607 15.4446 17.5202C15.7658 18.8971 14.6071 19.8987 13.4249 19.8987H4.07591C2.89369 19.8987 1.73504 18.8971 2.05628 17.5202C2.28015 16.5607 2.76117 15.2906 3.8071 14.2549ZM15.3042 11.4955C14.4702 11.4955 13.7006 11.2193 13.0821 10.7533C13.3742 10.3314 13.6054 9.86419 13.7632 9.36432C14.1597 9.75463 14.7039 9.99545 15.3042 9.99545C16.5176 9.99545 17.5012 9.01185 17.5012 7.79851C17.5012 6.58517 16.5176 5.60156 15.3042 5.60156C14.7039 5.60156 14.1597 5.84239 13.7632 6.23271C13.6054 5.73284 13.3741 5.26561 13.082 4.84371C13.7006 4.37777 14.4702 4.10156 15.3042 4.10156C17.346 4.10156 19.0012 5.75674 19.0012 7.79851C19.0012 9.84027 17.346 11.4955 15.3042 11.4955ZM19.9248 19.8987H16.3901C16.7014 19.4736 16.9159 18.969 16.9827 18.3987H19.9248C20.1341 18.3987 20.2991 18.3141 20.3936 18.2112C20.4796 18.1175 20.5169 18.0034 20.4837 17.861C20.2969 17.0607 19.913 16.088 19.1382 15.3208C18.4047 14.5945 17.261 13.9921 15.4231 13.9566C15.2232 13.6945 14.9995 13.437 14.7491 13.1891C14.5144 12.9566 14.262 12.7384 13.9916 12.5362C14.3853 12.4831 14.8044 12.4549 15.2503 12.4549C17.5447 12.4549 19.1291 13.2008 20.1936 14.2549C21.2395 15.2906 21.7206 16.5607 21.9444 17.5202C22.2657 18.8971 21.107 19.8987 19.9248 19.8987Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="flex items-end justify-between mt-5">
|
||||
<div>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">Customers</span>
|
||||
<h4 class="mt-2 font-bold text-gray-800 text-title-sm dark:text-white/90">3,782</h4>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="flex items-center gap-1 rounded-full bg-success-50 py-0.5 pl-2 pr-2.5 text-sm font-medium text-success-600 dark:bg-success-500/15 dark:text-success-500"
|
||||
>
|
||||
<svg
|
||||
class="fill-current"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M5.56462 1.62393C5.70193 1.47072 5.90135 1.37432 6.12329 1.37432C6.1236 1.37432 6.12391 1.37432 6.12422 1.37432C6.31631 1.37415 6.50845 1.44731 6.65505 1.59381L9.65514 4.5918C9.94814 4.88459 9.94831 5.35947 9.65552 5.65246C9.36273 5.94546 8.88785 5.94562 8.59486 5.65283L6.87329 3.93247L6.87329 10.125C6.87329 10.5392 6.53751 10.875 6.12329 10.875C5.70908 10.875 5.37329 10.5392 5.37329 10.125L5.37329 3.93578L3.65516 5.65282C3.36218 5.94562 2.8873 5.94547 2.5945 5.65248C2.3017 5.35949 2.30185 4.88462 2.59484 4.59182L5.56462 1.62393Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
|
||||
11.01%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800"
|
||||
>
|
||||
<svg
|
||||
class="fill-gray-800 dark:fill-white/90"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11.665 3.75621C11.8762 3.65064 12.1247 3.65064 12.3358 3.75621L18.7807 6.97856L12.3358 10.2009C12.1247 10.3065 11.8762 10.3065 11.665 10.2009L5.22014 6.97856L11.665 3.75621ZM4.29297 8.19203V16.0946C4.29297 16.3787 4.45347 16.6384 4.70757 16.7654L11.25 20.0366V11.6513C11.1631 11.6205 11.0777 11.5843 10.9942 11.5426L4.29297 8.19203ZM12.75 20.037L19.2933 16.7654C19.5474 16.6384 19.7079 16.3787 19.7079 16.0946V8.19202L13.0066 11.5426C12.9229 11.5844 12.8372 11.6208 12.75 11.6516V20.037ZM13.0066 2.41456C12.3732 2.09786 11.6277 2.09786 10.9942 2.41456L4.03676 5.89319C3.27449 6.27432 2.79297 7.05342 2.79297 7.90566V16.0946C2.79297 16.9469 3.27448 17.726 4.03676 18.1071L10.9942 21.5857L11.3296 20.9149L10.9942 21.5857C11.6277 21.9024 12.3732 21.9024 13.0066 21.5857L19.9641 18.1071C20.7264 17.726 21.2079 16.9469 21.2079 16.0946V7.90566C21.2079 7.05342 20.7264 6.27432 19.9641 5.89319L13.0066 2.41456Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="flex items-end justify-between mt-5">
|
||||
<div>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">Orders</span>
|
||||
<h4 class="mt-2 font-bold text-gray-800 text-title-sm dark:text-white/90">5,359</h4>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="flex items-center gap-1 rounded-full bg-error-50 py-0.5 pl-2 pr-2.5 text-sm font-medium text-error-600 dark:bg-error-500/15 dark:text-error-500"
|
||||
>
|
||||
<svg
|
||||
class="fill-current"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M5.31462 10.3761C5.45194 10.5293 5.65136 10.6257 5.87329 10.6257C5.8736 10.6257 5.8739 10.6257 5.87421 10.6257C6.0663 10.6259 6.25845 10.5527 6.40505 10.4062L9.40514 7.4082C9.69814 7.11541 9.69831 6.64054 9.40552 6.34754C9.11273 6.05454 8.63785 6.05438 8.34486 6.34717L6.62329 8.06753L6.62329 1.875C6.62329 1.46079 6.28751 1.125 5.87329 1.125C5.45908 1.125 5.12329 1.46079 5.12329 1.875L5.12329 8.06422L3.40516 6.34719C3.11218 6.05439 2.6373 6.05454 2.3445 6.34752C2.0517 6.64051 2.05185 7.11538 2.34484 7.40818L5.31462 10.3761Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
|
||||
9.05%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
18
resources/views/components/ecommerce/monthly-sale.blade.php
Normal file
18
resources/views/components/ecommerce/monthly-sale.blade.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pt-5 sm:px-6 sm:pt-6 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Monthly Sales
|
||||
</h3>
|
||||
|
||||
<!-- Dropdown Menu -->
|
||||
<x-common.dropdown-menu />
|
||||
<!-- End Dropdown Menu -->
|
||||
</div>
|
||||
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<div id="chartOne" class="-ml-5 h-full min-w-[690px] pl-2 xl:min-w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<div class="rounded-2xl border border-gray-200 bg-gray-100 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div class="shadow-default rounded-2xl bg-white px-5 pb-11 pt-5 dark:bg-gray-900 sm:px-6 sm:pt-6">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Monthly Target
|
||||
</h3>
|
||||
<p class="mt-1 text-theme-sm text-gray-500 dark:text-gray-400">
|
||||
Target you’ve set for each month
|
||||
</p>
|
||||
</div>
|
||||
<!-- Dropdown Menu -->
|
||||
<x-common.dropdown-menu />
|
||||
<!-- End Dropdown Menu -->
|
||||
|
||||
</div>
|
||||
<div class="relative max-h-[195px]">
|
||||
{{-- Chart --}}
|
||||
<div id="chartTwo" class="h-full"></div>
|
||||
<span class="absolute left-1/2 top-[85%] -translate-x-1/2 -translate-y-[85%] rounded-full bg-success-50 px-3 py-1 text-xs font-medium text-success-600 dark:bg-success-500/15 dark:text-success-500">+10%</span>
|
||||
</div>
|
||||
<p class="mx-auto mt-1.5 w-full max-w-[380px] text-center text-sm text-gray-500 sm:text-base">
|
||||
You earn $3287 today, it's higher than last month. Keep up your good work!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center gap-5 px-6 py-3.5 sm:gap-8 sm:py-5">
|
||||
<div>
|
||||
<p class="mb-1 text-center text-theme-xs text-gray-500 dark:text-gray-400 sm:text-sm">
|
||||
Target
|
||||
</p>
|
||||
<p
|
||||
class="flex items-center justify-center gap-1 text-base font-semibold text-gray-800 dark:text-white/90 sm:text-lg">
|
||||
$20K
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M7.26816 13.6632C7.4056 13.8192 7.60686 13.9176 7.8311 13.9176C7.83148 13.9176 7.83187 13.9176 7.83226 13.9176C8.02445 13.9178 8.21671 13.8447 8.36339 13.6981L12.3635 9.70076C12.6565 9.40797 12.6567 8.9331 12.3639 8.6401C12.0711 8.34711 11.5962 8.34694 11.3032 8.63973L8.5811 11.36L8.5811 2.5C8.5811 2.08579 8.24531 1.75 7.8311 1.75C7.41688 1.75 7.0811 2.08579 7.0811 2.5L7.0811 11.3556L4.36354 8.63975C4.07055 8.34695 3.59568 8.3471 3.30288 8.64009C3.01008 8.93307 3.01023 9.40794 3.30321 9.70075L7.26816 13.6632Z"
|
||||
fill="#D92D20" />
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="h-7 w-px bg-gray-200 dark:bg-gray-800"></div>
|
||||
|
||||
<div>
|
||||
<p class="mb-1 text-center text-theme-xs text-gray-500 dark:text-gray-400 sm:text-sm">
|
||||
Revenue
|
||||
</p>
|
||||
<p
|
||||
class="flex items-center justify-center gap-1 text-base font-semibold text-gray-800 dark:text-white/90 sm:text-lg">
|
||||
$20K
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M7.60141 2.33683C7.73885 2.18084 7.9401 2.08243 8.16435 2.08243C8.16475 2.08243 8.16516 2.08243 8.16556 2.08243C8.35773 2.08219 8.54998 2.15535 8.69664 2.30191L12.6968 6.29924C12.9898 6.59203 12.9899 7.0669 12.6971 7.3599C12.4044 7.6529 11.9295 7.65306 11.6365 7.36027L8.91435 4.64004L8.91435 13.5C8.91435 13.9142 8.57856 14.25 8.16435 14.25C7.75013 14.25 7.41435 13.9142 7.41435 13.5L7.41435 4.64442L4.69679 7.36025C4.4038 7.65305 3.92893 7.6529 3.63613 7.35992C3.34333 7.06693 3.34348 6.59206 3.63646 6.29926L7.60141 2.33683Z"
|
||||
fill="#039855" />
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="h-7 w-px bg-gray-200 dark:bg-gray-800"></div>
|
||||
|
||||
<div>
|
||||
<p class="mb-1 text-center text-theme-xs text-gray-500 dark:text-gray-400 sm:text-sm">
|
||||
Today
|
||||
</p>
|
||||
<p
|
||||
class="flex items-center justify-center gap-1 text-base font-semibold text-gray-800 dark:text-white/90 sm:text-lg">
|
||||
$20K
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M7.60141 2.33683C7.73885 2.18084 7.9401 2.08243 8.16435 2.08243C8.16475 2.08243 8.16516 2.08243 8.16556 2.08243C8.35773 2.08219 8.54998 2.15535 8.69664 2.30191L12.6968 6.29924C12.9898 6.59203 12.9899 7.0669 12.6971 7.3599C12.4044 7.6529 11.9295 7.65306 11.6365 7.36027L8.91435 4.64004L8.91435 13.5C8.91435 13.9142 8.57856 14.25 8.16435 14.25C7.75013 14.25 7.41435 13.9142 7.41435 13.5L7.41435 4.64442L4.69679 7.36025C4.4038 7.65305 3.92893 7.6529 3.63613 7.35992C3.34333 7.06693 3.34348 6.59206 3.63646 6.29926L7.60141 2.33683Z"
|
||||
fill="#039855" />
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
137
resources/views/components/ecommerce/recent-orders.blade.php
Normal file
137
resources/views/components/ecommerce/recent-orders.blade.php
Normal file
@@ -0,0 +1,137 @@
|
||||
@props(['products' => []])
|
||||
|
||||
@php
|
||||
$defaultProducts = [
|
||||
[
|
||||
'name' => 'Macbook pro 13"',
|
||||
'variants' => 2,
|
||||
'image' => '/images/product/product-01.jpg',
|
||||
'category' => 'Laptop',
|
||||
'price' => '$2399.00',
|
||||
'status' => 'Delivered',
|
||||
],
|
||||
[
|
||||
'name' => 'Apple Watch Ultra',
|
||||
'variants' => 1,
|
||||
'image' => '/images/product/product-02.jpg',
|
||||
'category' => 'Watch',
|
||||
'price' => '$879.00',
|
||||
'status' => 'Pending',
|
||||
],
|
||||
[
|
||||
'name' => 'iPhone 15 Pro Max',
|
||||
'variants' => 2,
|
||||
'image' => '/images/product/product-03.jpg',
|
||||
'category' => 'SmartPhone',
|
||||
'price' => '$1869.00',
|
||||
'status' => 'Delivered',
|
||||
],
|
||||
[
|
||||
'name' => 'iPad Pro 3rd Gen',
|
||||
'variants' => 2,
|
||||
'image' => '/images/product/product-04.jpg',
|
||||
'category' => 'Electronics',
|
||||
'price' => '$1699.00',
|
||||
'status' => 'Canceled',
|
||||
],
|
||||
[
|
||||
'name' => 'Airpods Pro 2nd Gen',
|
||||
'variants' => 1,
|
||||
'image' => '/images/product/product-05.jpg',
|
||||
'category' => 'Accessories',
|
||||
'price' => '$240.00',
|
||||
'status' => 'Delivered',
|
||||
],
|
||||
];
|
||||
|
||||
$productsList = !empty($products) ? $products : $defaultProducts;
|
||||
|
||||
// Helper function for status classes
|
||||
$getStatusClasses = function($status) {
|
||||
$baseClasses = 'rounded-full px-2 py-0.5 text-theme-xs font-medium';
|
||||
|
||||
return match($status) {
|
||||
'Delivered' => $baseClasses . ' bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500',
|
||||
'Pending' => $baseClasses . ' bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-orange-400',
|
||||
'Canceled' => $baseClasses . ' bg-error-50 text-error-600 dark:bg-error-500/15 dark:text-error-500',
|
||||
default => $baseClasses . ' bg-gray-50 text-gray-600 dark:bg-gray-500/15 dark:text-gray-400',
|
||||
};
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-gray-200 bg-white px-4 pb-3 pt-4 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6">
|
||||
<div class="flex flex-col gap-2 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">Recent Orders</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<button class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="stroke-current fill-white dark:fill-gray-800" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.29004 5.90393H17.7067" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M17.7075 14.0961H2.29085" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M12.0826 3.33331C13.5024 3.33331 14.6534 4.48431 14.6534 5.90414C14.6534 7.32398 13.5024 8.47498 12.0826 8.47498C10.6627 8.47498 9.51172 7.32398 9.51172 5.90415C9.51172 4.48432 10.6627 3.33331 12.0826 3.33331Z" fill="" stroke="" stroke-width="1.5" />
|
||||
<path d="M7.91745 11.525C6.49762 11.525 5.34662 12.676 5.34662 14.0959C5.34661 15.5157 6.49762 16.6667 7.91745 16.6667C9.33728 16.6667 10.4883 15.5157 10.4883 14.0959C10.4883 12.676 9.33728 11.525 7.91745 11.525Z" fill="" stroke="" stroke-width="1.5" />
|
||||
</svg>
|
||||
Filter
|
||||
</button>
|
||||
|
||||
<button class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
See all
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<table class="min-w-full">
|
||||
<thead>
|
||||
<tr class="border-t border-gray-100 dark:border-gray-800">
|
||||
<th class="py-3 text-left">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Products</p>
|
||||
</th>
|
||||
<th class="py-3 text-left">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Category</p>
|
||||
</th>
|
||||
<th class="py-3 text-left">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Price</p>
|
||||
</th>
|
||||
<th class="py-3 text-left">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Status</p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($productsList as $product)
|
||||
<tr class="border-t border-gray-100 dark:border-gray-800">
|
||||
<td class="py-3 whitespace-nowrap">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="h-[50px] w-[50px] overflow-hidden rounded-md">
|
||||
<img src="{{ $product['image'] }}" alt="{{ $product['name'] }}" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
||||
{{ $product['name'] }}
|
||||
</p>
|
||||
<span class="text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
{{ $product['variants'] }} Variants
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 whitespace-nowrap">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">{{ $product['category'] }}</p>
|
||||
</td>
|
||||
<td class="py-3 whitespace-nowrap">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">{{ $product['price'] }}</p>
|
||||
</td>
|
||||
<td class="py-3 whitespace-nowrap">
|
||||
<span class="{{ $getStatusClasses($product['status']) }}">
|
||||
{{ $product['status'] }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
<div
|
||||
class="rounded-2xl border border-gray-200 bg-white px-5 pb-5 pt-5 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6 sm:pt-6">
|
||||
<div class="flex flex-col gap-5 mb-6 sm:flex-row sm:justify-between">
|
||||
<div class="w-full">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Statistics
|
||||
</h3>
|
||||
<p class="mt-1 text-gray-500 text-theme-sm dark:text-gray-400">
|
||||
Target you’ve set for each month
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start w-full gap-3 sm:justify-end">
|
||||
<div x-data="{ selected: 'overview' }"
|
||||
class="inline-flex w-fit items-center gap-0.5 rounded-lg bg-gray-100 p-0.5 dark:bg-gray-900">
|
||||
|
||||
@php
|
||||
$options = [
|
||||
['value' => 'overview', 'label' => 'Overview'],
|
||||
['value' => 'sales', 'label' => 'Sales'],
|
||||
['value' => 'revenue', 'label' => 'Revenue'],
|
||||
];
|
||||
@endphp
|
||||
|
||||
@foreach ($options as $option)
|
||||
<button @click="selected = '{{ $option['value'] }}'"
|
||||
:class="selected === '{{ $option['value'] }}' ? 'shadow-theme-xs text-gray-900 dark:text-white bg-white dark:bg-gray-800' :
|
||||
'text-gray-500 dark:text-gray-400'"
|
||||
class="px-3 py-2 font-medium rounded-md text-theme-sm hover:text-gray-900 dark:hover:text-white">
|
||||
{{ $option['label'] }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<div id="chartThree" class="-ml-4 min-w-[700px] pl-2 xl:min-w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
60
resources/views/components/form/date-picker.blade.php
Normal file
60
resources/views/components/form/date-picker.blade.php
Normal file
@@ -0,0 +1,60 @@
|
||||
@props([
|
||||
'id' => 'datepicker-' . uniqid(),
|
||||
'mode' => 'single', // 'single', 'multiple', 'range', 'time'
|
||||
'defaultDate' => null,
|
||||
'label' => null,
|
||||
'placeholder' => 'Select date',
|
||||
'name' => null,
|
||||
'dateFormat' => 'Y-m-d',
|
||||
])
|
||||
|
||||
<div x-data="{
|
||||
flatpickrInstance: null,
|
||||
init() {
|
||||
this.$nextTick(() => {
|
||||
this.flatpickrInstance = flatpickr(this.$refs.dateInput, {
|
||||
mode: '{{ $mode }}',
|
||||
static: true,
|
||||
monthSelectorType: 'static',
|
||||
dateFormat: '{{ $dateFormat }}',
|
||||
defaultDate: {{ $defaultDate ? (is_array($defaultDate) ? json_encode($defaultDate) : "'" . $defaultDate . "'") : 'null' }},
|
||||
onChange: (selectedDates, dateStr, instance) => {
|
||||
this.$dispatch('date-change', {
|
||||
selectedDates,
|
||||
dateStr,
|
||||
instance
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
destroy() {
|
||||
if (this.flatpickrInstance) {
|
||||
this.flatpickrInstance.destroy();
|
||||
this.flatpickrInstance = null;
|
||||
}
|
||||
}
|
||||
}" x-init="init()" x-destroy="destroy()">
|
||||
@if($label)
|
||||
<label for="{{ $id }}" class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
{{ $label }}
|
||||
</label>
|
||||
@endif
|
||||
|
||||
<div class="relative custom-datepicker">
|
||||
<input
|
||||
x-ref="dateInput"
|
||||
type="text"
|
||||
id="{{ $id }}"
|
||||
name="{{ $name }}"
|
||||
placeholder="{{ $placeholder }}"
|
||||
class="h-11 w-full rounded-lg border appearance-none px-4 py-2.5 text-sm shadow-theme-xs placeholder:text-gray-400 focus:outline-hidden focus:ring-3 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 bg-transparent text-gray-800 border-gray-300 focus:border-brand-300 focus:ring-brand-500/20 dark:border-gray-700 dark:focus:border-brand-800"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<span class="absolute text-gray-500 -translate-y-1/2 pointer-events-none right-3 top-1/2 dark:text-gray-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" class="size-6">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2C8.41421 2 8.75 2.33579 8.75 2.75V3.75H15.25V2.75C15.25 2.33579 15.5858 2 16 2C16.4142 2 16.75 2.33579 16.75 2.75V3.75H18.5C19.7426 3.75 20.75 4.75736 20.75 6V9V19C20.75 20.2426 19.7426 21.25 18.5 21.25H5.5C4.25736 21.25 3.25 20.2426 3.25 19V9V6C3.25 4.75736 4.25736 3.75 5.5 3.75H7.25V2.75C7.25 2.33579 7.58579 2 8 2ZM8 5.25H5.5C5.08579 5.25 4.75 5.58579 4.75 6V8.25H19.25V6C19.25 5.58579 18.9142 5.25 18.5 5.25H16H8ZM19.25 9.75H4.75V19C4.75 19.4142 5.08579 19.75 5.5 19.75H18.5C18.9142 19.75 19.25 19.4142 19.25 19V9.75Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
<x-common.component-card title="Checkboxes">
|
||||
<div class="flex flex-wrap items-center gap-8">
|
||||
<div x-data="{ checkboxToggle: false }">
|
||||
<label for="checkboxLabelOne"
|
||||
class="flex cursor-pointer items-center text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="checkboxLabelOne" class="sr-only"
|
||||
@change="checkboxToggle = !checkboxToggle" />
|
||||
<div :class="checkboxToggle ? 'border-brand-500 bg-brand-500' :
|
||||
'bg-transparent border-gray-300 dark:border-gray-700'"
|
||||
class="f hover:border-brand-500 dark:hover:border-brand-500 mr-3 flex h-5 w-5 items-center justify-center rounded-md border-[1.25px]">
|
||||
<span :class="checkboxToggle ? '' : 'opacity-0'">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" stroke="white" stroke-width="1.94437"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Default
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ checkboxToggle: true }">
|
||||
<label for="checkboxLabelTwo"
|
||||
class="flex cursor-pointer items-center text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="checkboxLabelTwo" class="sr-only"
|
||||
@change="checkboxToggle = !checkboxToggle" />
|
||||
<div :class="checkboxToggle ? 'border-brand-500 bg-brand-500' :
|
||||
'bg-transparent border-gray-300 dark:border-gray-700'"
|
||||
class="hover:border-brand-500 dark:hover:border-brand-500 mr-3 flex h-5 w-5 items-center justify-center rounded-md border-[1.25px]">
|
||||
<span :class="checkboxToggle ? '' : 'opacity-0'">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" stroke="white" stroke-width="1.94437"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Checked
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ checkboxToggle: true }">
|
||||
<label for="checkboxLabelThree"
|
||||
class="flex cursor-pointer items-center text-sm font-medium text-gray-300 select-none dark:text-gray-700">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="checkboxLabelThree" class="peer sr-only"
|
||||
@change="checkboxToggle = !checkboxToggle" disabled />
|
||||
<div :class="checkboxToggle ? 'bg-transparent border-gray-200 dark:border-gray-800' :
|
||||
'border-brand-500 bg-brand-500'"
|
||||
class="mr-3 flex h-5 w-5 items-center justify-center rounded-md border-[1.25px]">
|
||||
<span :class="checkboxToggle ? '' : 'opacity-0'">
|
||||
<svg class="stroke-gray-200 dark:stroke-gray-800" width="14" height="14"
|
||||
viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" stroke="" stroke-width="2.33333"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,133 @@
|
||||
<x-common.component-card title="Default Inputs">
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Input
|
||||
</label>
|
||||
<input type="text"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Input with Placeholder
|
||||
</label>
|
||||
<input type="text" placeholder="info@gmail.com"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Select Input
|
||||
</label>
|
||||
<div x-data="{ isOptionSelected: false }" class="relative z-20 bg-transparent">
|
||||
<select
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pr-11 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30"
|
||||
:class="isOptionSelected && 'text-gray-800 dark:text-white/90'" @change="isOptionSelected = true">
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Select Option
|
||||
</option>
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Marketing
|
||||
</option>
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Template
|
||||
</option>
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Development
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
class="pointer-events-none absolute top-1/2 right-4 z-30 -translate-y-1/2 text-gray-500 dark:text-gray-400">
|
||||
<svg class="stroke-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.79175 7.396L10.0001 12.6043L15.2084 7.396" stroke="" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Password Input
|
||||
</label>
|
||||
<div x-data="{ showPassword: false }" class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" placeholder="Enter your password"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span @click="showPassword = !showPassword"
|
||||
class="absolute top-1/2 right-4 z-30 -translate-y-1/2 cursor-pointer">
|
||||
<svg x-show="!showPassword" class="fill-gray-500 dark:fill-gray-400" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z" />
|
||||
</svg>
|
||||
|
||||
<svg x-show="showPassword" class="fill-gray-500 dark:fill-gray-400" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M4.63803 3.57709C4.34513 3.2842 3.87026 3.2842 3.57737 3.57709C3.28447 3.86999 3.28447 4.34486 3.57737 4.63775L4.85323 5.91362C3.74609 6.84199 2.89363 8.06395 2.4155 9.45936C2.3615 9.61694 2.3615 9.78801 2.41549 9.94558C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C11.255 15.3619 12.4422 15.0737 13.4994 14.5598L15.3625 16.4229C15.6554 16.7158 16.1302 16.7158 16.4231 16.4229C16.716 16.13 16.716 15.6551 16.4231 15.3622L4.63803 3.57709ZM12.3608 13.4212L10.4475 11.5079C10.3061 11.5423 10.1584 11.5606 10.0064 11.5606H9.99151C8.96527 11.5606 8.13333 10.7286 8.13333 9.70237C8.13333 9.5461 8.15262 9.39434 8.18895 9.24933L5.91885 6.97923C5.03505 7.69015 4.34057 8.62704 3.92328 9.70247C4.86803 12.1373 7.23361 13.8619 10.0002 13.8619C10.8326 13.8619 11.6287 13.7058 12.3608 13.4212ZM16.0771 9.70249C15.7843 10.4569 15.3552 11.1432 14.8199 11.7311L15.8813 12.7925C16.6329 11.9813 17.2187 11.0143 17.5849 9.94561C17.6389 9.78803 17.6389 9.61696 17.5849 9.45938C16.5055 6.30925 13.5184 4.04303 10.0002 4.04303C9.13525 4.04303 8.30244 4.17999 7.52218 4.43338L8.75139 5.66259C9.1556 5.58413 9.57311 5.54303 10.0002 5.54303C12.7667 5.54303 15.1323 7.26768 16.0771 9.70249Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Date Picker Input
|
||||
</label>
|
||||
|
||||
<x-form.date-picker
|
||||
id="date_pick"
|
||||
name="date_pick"
|
||||
placeholder="Date Picker"
|
||||
defaultDate="{{ now()->format('Y-m-d') }}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Time Select Input
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input type="time" placeholder="12:00 AM" onclick="this.showPicker()"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span class="absolute top-1/2 right-3 -translate-y-1/2 text-gray-500 dark:text-gray-400">
|
||||
<svg class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M3.04175 9.99984C3.04175 6.15686 6.1571 3.0415 10.0001 3.0415C13.8431 3.0415 16.9584 6.15686 16.9584 9.99984C16.9584 13.8428 13.8431 16.9582 10.0001 16.9582C6.1571 16.9582 3.04175 13.8428 3.04175 9.99984ZM10.0001 1.5415C5.32867 1.5415 1.54175 5.32843 1.54175 9.99984C1.54175 14.6712 5.32867 18.4582 10.0001 18.4582C14.6715 18.4582 18.4584 14.6712 18.4584 9.99984C18.4584 5.32843 14.6715 1.5415 10.0001 1.5415ZM9.99998 10.7498C9.58577 10.7498 9.24998 10.4141 9.24998 9.99984V5.4165C9.24998 5.00229 9.58577 4.6665 9.99998 4.6665C10.4142 4.6665 10.75 5.00229 10.75 5.4165V9.24984H13.3334C13.7476 9.24984 14.0834 9.58562 14.0834 9.99984C14.0834 10.4141 13.7476 10.7498 13.3334 10.7498H10.0001H9.99998Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Input with Payment
|
||||
</label>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" placeholder="Card number"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pl-[62px] text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span
|
||||
class="absolute top-1/2 left-0 flex h-11 w-[46px] -translate-y-1/2 items-center justify-center border-r border-gray-200 dark:border-gray-800">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="6.25" cy="10" r="5.625" fill="#E80B26" />
|
||||
<circle cx="13.75" cy="10" r="5.625" fill="#F59D31" />
|
||||
<path
|
||||
d="M10 14.1924C11.1508 13.1625 11.875 11.6657 11.875 9.99979C11.875 8.33383 11.1508 6.8371 10 5.80713C8.84918 6.8371 8.125 8.33383 8.125 9.99979C8.125 11.6657 8.84918 13.1625 10 14.1924Z"
|
||||
fill="#FC6020" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
119
resources/views/components/form/form-elements/dropzone.blade.php
Normal file
119
resources/views/components/form/form-elements/dropzone.blade.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<x-common.component-card title="Dropzone">
|
||||
<!-- Dropzone -->
|
||||
<div
|
||||
x-data="{
|
||||
isDragging: false,
|
||||
files: [],
|
||||
handleDrop(e) {
|
||||
this.isDragging = false;
|
||||
const droppedFiles = Array.from(e.dataTransfer.files);
|
||||
this.handleFiles(droppedFiles);
|
||||
},
|
||||
handleFiles(selectedFiles) {
|
||||
const validTypes = ['image/png', 'image/jpeg', 'image/webp', 'image/svg+xml'];
|
||||
const validFiles = selectedFiles.filter(file => validTypes.includes(file.type));
|
||||
|
||||
if (validFiles.length > 0) {
|
||||
this.files = [...this.files, ...validFiles];
|
||||
console.log('Files uploaded:', validFiles);
|
||||
|
||||
// Here you can add logic to upload files to server
|
||||
this.uploadFiles(validFiles);
|
||||
}
|
||||
},
|
||||
uploadFiles(files) {
|
||||
// Implement your file upload logic here
|
||||
// Example: Use FormData and fetch/axios to upload
|
||||
console.log('Uploading files:', files);
|
||||
},
|
||||
removeFile(index) {
|
||||
this.files.splice(index, 1);
|
||||
}
|
||||
}"
|
||||
class="transition border border-gray-300 border-dashed cursor-pointer dark:hover:border-brand-500 dark:border-gray-700 rounded-xl hover:border-brand-500"
|
||||
>
|
||||
<div
|
||||
@drop.prevent="handleDrop($event)"
|
||||
@dragover.prevent="isDragging = true"
|
||||
@dragleave.prevent="isDragging = false"
|
||||
@click="$refs.fileInput.click()"
|
||||
:class="isDragging
|
||||
? 'border-brand-500 bg-gray-100 dark:bg-gray-800'
|
||||
: 'border-gray-300 bg-gray-50 dark:border-gray-700 dark:bg-gray-900'"
|
||||
class="dropzone rounded-xl border-dashed border-gray-300 p-7 lg:p-10 transition-colors cursor-pointer"
|
||||
id="demo-upload"
|
||||
>
|
||||
<!-- Hidden File Input -->
|
||||
<input
|
||||
x-ref="fileInput"
|
||||
type="file"
|
||||
@change="handleFiles(Array.from($event.target.files)); $event.target.value = ''"
|
||||
accept="image/png,image/jpeg,image/webp,image/svg+xml"
|
||||
multiple
|
||||
class="hidden"
|
||||
@click.stop
|
||||
/>
|
||||
|
||||
<div class="flex flex-col items-center m-0">
|
||||
<!-- Icon Container -->
|
||||
<div class="mb-[22px] flex justify-center">
|
||||
<div class="flex h-[68px] w-[68px] items-center justify-center rounded-full bg-gray-200 text-gray-700 dark:bg-gray-800 dark:text-gray-400">
|
||||
<svg
|
||||
class="fill-current"
|
||||
width="29"
|
||||
height="28"
|
||||
viewBox="0 0 29 28"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M14.5019 3.91699C14.2852 3.91699 14.0899 4.00891 13.953 4.15589L8.57363 9.53186C8.28065 9.82466 8.2805 10.2995 8.5733 10.5925C8.8661 10.8855 9.34097 10.8857 9.63396 10.5929L13.7519 6.47752V18.667C13.7519 19.0812 14.0877 19.417 14.5019 19.417C14.9161 19.417 15.2519 19.0812 15.2519 18.667V6.48234L19.3653 10.5929C19.6583 10.8857 20.1332 10.8855 20.426 10.5925C20.7188 10.2995 20.7186 9.82463 20.4256 9.53184L15.0838 4.19378C14.9463 4.02488 14.7367 3.91699 14.5019 3.91699ZM5.91626 18.667C5.91626 18.2528 5.58047 17.917 5.16626 17.917C4.75205 17.917 4.41626 18.2528 4.41626 18.667V21.8337C4.41626 23.0763 5.42362 24.0837 6.66626 24.0837H22.3339C23.5766 24.0837 24.5839 23.0763 24.5839 21.8337V18.667C24.5839 18.2528 24.2482 17.917 23.8339 17.917C23.4197 17.917 23.0839 18.2528 23.0839 18.667V21.8337C23.0839 22.2479 22.7482 22.5837 22.3339 22.5837H6.66626C6.25205 22.5837 5.91626 22.2479 5.91626 21.8337V18.667Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Text Content -->
|
||||
<h4 class="mb-3 font-semibold text-gray-800 text-theme-xl dark:text-white/90">
|
||||
<span x-show="!isDragging">Drag & Drop Files Here</span>
|
||||
<span x-show="isDragging" x-cloak>Drop Files Here</span>
|
||||
</h4>
|
||||
|
||||
<span class="text-center mb-5 block w-full max-w-[290px] text-sm text-gray-700 dark:text-gray-400">
|
||||
Drag and drop your PNG, JPG, WebP, SVG images here or browse
|
||||
</span>
|
||||
|
||||
<span class="font-medium underline text-theme-sm text-brand-500">
|
||||
Browse File
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Preview List (Optional) -->
|
||||
<div x-show="files.length > 0" class="mt-4 p-4 border-t border-gray-200 dark:border-gray-700" x-cloak>
|
||||
<h5 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3">Uploaded Files:</h5>
|
||||
<ul class="space-y-2">
|
||||
<template x-for="(file, index) in files" :key="index">
|
||||
<li class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300" x-text="file.name"></span>
|
||||
</div>
|
||||
<button
|
||||
@click.stop="removeFile(index)"
|
||||
type="button"
|
||||
class="text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,10 @@
|
||||
<x-common.component-card title="File Input">
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Upload file
|
||||
</label>
|
||||
<input type="file"
|
||||
class="focus:border-ring-brand-300 shadow-theme-xs focus:file:ring-brand-300 h-11 w-full overflow-hidden rounded-lg border border-gray-300 bg-transparent text-sm text-gray-500 transition-colors file:mr-5 file:border-collapse file:cursor-pointer file:rounded-l-lg file:border-0 file:border-r file:border-solid file:border-gray-200 file:bg-gray-50 file:py-3 file:pr-3 file:pl-3.5 file:text-sm file:text-gray-700 placeholder:text-gray-400 hover:file:bg-gray-100 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-gray-400 dark:text-white/90 dark:file:border-gray-800 dark:file:bg-white/[0.03] dark:file:text-gray-400 dark:placeholder:text-gray-400" />
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,144 @@
|
||||
<x-common.component-card title="Input Group">
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute top-1/2 left-0 -translate-y-1/2 border-r border-gray-200 px-3.5 py-3 text-gray-500 dark:border-gray-800 dark:text-gray-400">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M3.04175 7.06206V14.375C3.04175 14.6511 3.26561 14.875 3.54175 14.875H16.4584C16.7346 14.875 16.9584 14.6511 16.9584 14.375V7.06245L11.1443 11.1168C10.457 11.5961 9.54373 11.5961 8.85638 11.1168L3.04175 7.06206ZM16.9584 5.19262C16.9584 5.19341 16.9584 5.1942 16.9584 5.19498V5.20026C16.9572 5.22216 16.946 5.24239 16.9279 5.25501L10.2864 9.88638C10.1145 10.0062 9.8862 10.0062 9.71437 9.88638L3.07255 5.25485C3.05342 5.24151 3.04202 5.21967 3.04202 5.19636C3.042 5.15695 3.07394 5.125 3.11335 5.125H16.8871C16.9253 5.125 16.9564 5.15494 16.9584 5.19262ZM18.4584 5.21428V14.375C18.4584 15.4796 17.563 16.375 16.4584 16.375H3.54175C2.43718 16.375 1.54175 15.4796 1.54175 14.375V5.19498C1.54175 5.1852 1.54194 5.17546 1.54231 5.16577C1.55858 4.31209 2.25571 3.625 3.11335 3.625H16.8871C17.7549 3.625 18.4584 4.32843 18.4585 5.19622C18.4585 5.20225 18.4585 5.20826 18.4584 5.21428Z"
|
||||
fill="#667085" />
|
||||
</svg>
|
||||
</span>
|
||||
<input type="text" placeholder="info@gmail.com" class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 pl-[62px] text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div x-data="{
|
||||
selectedCountry: 'US',
|
||||
countryCodes: {
|
||||
'US': '+1',
|
||||
'GB': '+44',
|
||||
'CA': '+1',
|
||||
'AU': '+61'
|
||||
},
|
||||
phoneNumber: ''
|
||||
}">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Phone
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute">
|
||||
<select x-model="selectedCountry" @change="phoneNumber = countryCodes[selectedCountry]"
|
||||
class="focus:border-brand-300 focus:ring-brand-500/10 appearance-none rounded-l-lg border-0 border-r border-gray-200 bg-transparent bg-none py-3 pr-8 pl-3.5 leading-tight text-gray-700 focus:ring-3 focus:outline-hidden dark:border-gray-800 dark:text-gray-400">
|
||||
<option value="US" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
US
|
||||
</option>
|
||||
<option value="GB" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
GB
|
||||
</option>
|
||||
<option value="CA" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
CA
|
||||
</option>
|
||||
<option value="AU" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
AU
|
||||
</option>
|
||||
<!-- Add more country codes as needed -->
|
||||
</select>
|
||||
<div
|
||||
class="pointer-events-none absolute inset-y-0 right-3 flex items-center text-gray-700 dark:text-gray-400">
|
||||
<svg class="stroke-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.79175 7.396L10.0001 12.6043L15.2084 7.396" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<input placeholder="+1 (555) 000-0000" x-model="phoneNumber" type="tel"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent py-3 pr-4 pl-[84px] text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div x-data="{
|
||||
selectedCountry: 'US',
|
||||
countryCodes: {
|
||||
'US': '+1',
|
||||
'GB': '+44',
|
||||
'CA': '+1',
|
||||
'AU': '+61'
|
||||
},
|
||||
phoneNumber: ''
|
||||
}">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Phone
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute right-0">
|
||||
<select x-model="selectedCountry" @change="phoneNumber = countryCodes[selectedCountry]"
|
||||
class="focus:border-brand-300 focus:ring-brand-500/10 appearance-none rounded-r-lg border-0 border-l border-gray-200 bg-transparent bg-none py-3 pr-8 pl-3.5 leading-tight text-gray-700 focus:ring-3 focus:outline-hidden dark:border-gray-800 dark:text-gray-400">
|
||||
<option value="US" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
US
|
||||
</option>
|
||||
<option value="GB" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
GB
|
||||
</option>
|
||||
<option value="CA" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
CA
|
||||
</option>
|
||||
<option value="AU" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
AU
|
||||
</option>
|
||||
<!-- Add more country codes as needed -->
|
||||
</select>
|
||||
<div
|
||||
class="pointer-events-none absolute inset-y-0 right-3 flex items-center text-gray-700 dark:text-gray-400">
|
||||
<svg class="stroke-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.79175 7.396L10.0001 12.6043L15.2084 7.396" stroke="" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<input placeholder="+1 (555) 000-0000" x-model="phoneNumber" type="tel"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent py-3 px-3 pr-[84px] text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
URL
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span
|
||||
class="absolute top-1/2 left-0 inline-flex h-11 -translate-y-1/2 items-center justify-center border-r border-gray-200 py-3 pr-3 pl-3.5 text-gray-500 dark:border-gray-800 dark:text-gray-400">
|
||||
http://
|
||||
</span>
|
||||
<input type="url" placeholder="www.tailadmin.com"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 pl-[90px] text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div id="copy-input">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Website
|
||||
</label>
|
||||
<div class="relative">
|
||||
<button id="copy-button"
|
||||
class="absolute top-1/2 right-0 inline-flex -translate-y-1/2 cursor-pointer items-center gap-1 border-l border-gray-200 py-3 pr-3 pl-3.5 text-sm font-medium text-gray-700 dark:border-gray-800 dark:text-gray-400">
|
||||
<svg class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M6.58822 4.58398C6.58822 4.30784 6.81207 4.08398 7.08822 4.08398H15.4154C15.6915 4.08398 15.9154 4.30784 15.9154 4.58398L15.9154 12.9128C15.9154 13.189 15.6916 13.4128 15.4154 13.4128H7.08821C6.81207 13.4128 6.58822 13.189 6.58822 12.9128V4.58398ZM7.08822 2.58398C5.98365 2.58398 5.08822 3.47942 5.08822 4.58398V5.09416H4.58496C3.48039 5.09416 2.58496 5.98959 2.58496 7.09416V15.4161C2.58496 16.5207 3.48039 17.4161 4.58496 17.4161H12.9069C14.0115 17.4161 14.9069 16.5207 14.9069 15.4161L14.9069 14.9128H15.4154C16.52 14.9128 17.4154 14.0174 17.4154 12.9128L17.4154 4.58398C17.4154 3.47941 16.52 2.58398 15.4154 2.58398H7.08822ZM13.4069 14.9128H7.08821C5.98364 14.9128 5.08822 14.0174 5.08822 12.9128V6.59416H4.58496C4.30882 6.59416 4.08496 6.81801 4.08496 7.09416V15.4161C4.08496 15.6922 4.30882 15.9161 4.58496 15.9161H12.9069C13.183 15.9161 13.4069 15.6922 13.4069 15.4161L13.4069 14.9128Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
<div id="copy-text">Copy</div>
|
||||
</button>
|
||||
<input value="www.tailadmin.com" type="url" id="website-input"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent py-3 pr-[90px] pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,61 @@
|
||||
<x-common.component-card
|
||||
title="Input States"
|
||||
desc="Validation styles for error, success and disabled states on form controls."
|
||||
>
|
||||
<div class="space-y-5 sm:space-y-6">
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input type="text" value="demoemail"
|
||||
class="dark:bg-dark-900 border-error-300 shadow-theme-xs focus:border-error-300 focus:ring-error-500/10 dark:border-error-700 dark:focus:border-error-800 w-full rounded-lg border bg-transparent px-4 py-2.5 pr-10 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span class="absolute top-1/2 right-3.5 -translate-y-1/2">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M2.58325 7.99967C2.58325 5.00813 5.00838 2.58301 7.99992 2.58301C10.9915 2.58301 13.4166 5.00813 13.4166 7.99967C13.4166 10.9912 10.9915 13.4163 7.99992 13.4163C5.00838 13.4163 2.58325 10.9912 2.58325 7.99967ZM7.99992 1.08301C4.17995 1.08301 1.08325 4.17971 1.08325 7.99967C1.08325 11.8196 4.17995 14.9163 7.99992 14.9163C11.8199 14.9163 14.9166 11.8196 14.9166 7.99967C14.9166 4.17971 11.8199 1.08301 7.99992 1.08301ZM7.09932 5.01639C7.09932 5.51345 7.50227 5.91639 7.99932 5.91639H7.99999C8.49705 5.91639 8.89999 5.51345 8.89999 5.01639C8.89999 4.51933 8.49705 4.11639 7.99999 4.11639H7.99932C7.50227 4.11639 7.09932 4.51933 7.09932 5.01639ZM7.99998 11.8306C7.58576 11.8306 7.24998 11.4948 7.24998 11.0806V7.29627C7.24998 6.88206 7.58576 6.54627 7.99998 6.54627C8.41419 6.54627 8.74998 6.88206 8.74998 7.29627V11.0806C8.74998 11.4948 8.41419 11.8306 7.99998 11.8306Z"
|
||||
fill="#F04438" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="text-theme-xs text-error-500 mt-1.5">
|
||||
This is an error message.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input type="text" value="demoemail@gmail.com"
|
||||
class="dark:bg-dark-900 border-success-300 shadow-theme-xs focus:border-success-300 focus:ring-success-500/10 dark:border-success-700 dark:focus:border-success-800 w-full rounded-lg border bg-transparent px-4 py-2.5 pr-10 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span class="absolute top-1/2 right-3.5 -translate-y-1/2">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M2.61792 8.00034C2.61792 5.02784 5.0276 2.61816 8.00009 2.61816C10.9726 2.61816 13.3823 5.02784 13.3823 8.00034C13.3823 10.9728 10.9726 13.3825 8.00009 13.3825C5.0276 13.3825 2.61792 10.9728 2.61792 8.00034ZM8.00009 1.11816C4.19917 1.11816 1.11792 4.19942 1.11792 8.00034C1.11792 11.8013 4.19917 14.8825 8.00009 14.8825C11.801 14.8825 14.8823 11.8013 14.8823 8.00034C14.8823 4.19942 11.801 1.11816 8.00009 1.11816ZM10.5192 7.266C10.8121 6.97311 10.8121 6.49823 10.5192 6.20534C10.2264 5.91245 9.75148 5.91245 9.45858 6.20534L7.45958 8.20434L6.54162 7.28638C6.24873 6.99349 5.77385 6.99349 5.48096 7.28638C5.18807 7.57927 5.18807 8.05415 5.48096 8.34704L6.92925 9.79533C7.0699 9.93599 7.26067 10.015 7.45958 10.015C7.6585 10.015 7.84926 9.93599 7.98991 9.79533L10.5192 7.266Z"
|
||||
fill="#12B76A" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="text-theme-xs text-success-500 mt-1.5">
|
||||
This is an success message.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-300 dark:text-white/15">
|
||||
Email
|
||||
</label>
|
||||
<input type="text" placeholder="info@gmail.com" disabled
|
||||
class="shadow-theme-xs focus:border-brand-300 focus:shadow-focus-ring dark:focus:border-brand-300 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:outline-hidden disabled:border-gray-100 disabled:placeholder:text-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-gray-400 dark:disabled:border-gray-800 dark:disabled:placeholder:text-white/15" />
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,53 @@
|
||||
<x-common.component-card title="Radio Buttons">
|
||||
<div class="flex flex-wrap items-center gap-8">
|
||||
<div x-data="{ checkboxToggle: false }">
|
||||
<label for="radioLabelOne"
|
||||
class="flex cursor-pointer items-center text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="radioLabelOne" class="sr-only" @change="checkboxToggle = !checkboxToggle" />
|
||||
<div :class="checkboxToggle ? 'border-brand-500 bg-brand-500' :
|
||||
'bg-transparent border-gray-300 dark:border-gray-700'"
|
||||
class="hover:border-brand-500 dark:hover:border-brand-500 mr-3 flex h-5 w-5 items-center justify-center rounded-full border-[1.25px]">
|
||||
<span class="h-2 w-2 rounded-full"
|
||||
:class="checkboxToggle ? 'bg-white' : 'bg-white dark:bg-[#171f2e]'"></span>
|
||||
</div>
|
||||
</div>
|
||||
Default
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ checkboxToggle: true }">
|
||||
<label for="radioLabelTwo"
|
||||
class="flex cursor-pointer items-center text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="radioLabelTwo" class="sr-only"
|
||||
@change="checkboxToggle = !checkboxToggle" />
|
||||
<div :class="checkboxToggle ? 'border-brand-500 bg-brand-500' :
|
||||
'bg-transparent border-gray-300 dark:border-gray-700'"
|
||||
class="hover:border-brand-500 dark:hover:border-brand-500 mr-3 flex h-5 w-5 items-center justify-center rounded-full border-[1.25px]">
|
||||
<span class="h-2 w-2 rounded-full"
|
||||
:class="checkboxToggle ? 'bg-white' : 'bg-white dark:bg-[#171f2e]'"></span>
|
||||
</div>
|
||||
</div>
|
||||
Secondary
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ checkboxToggle: false }">
|
||||
<label for="radioLabelThree"
|
||||
class="flex cursor-pointer items-center text-sm font-medium text-gray-300 select-none dark:text-gray-700">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="radioLabelThree" class="peer sr-only"
|
||||
@change="checkboxToggle = !checkboxToggle" disabled />
|
||||
<div :class="checkboxToggle ? 'bg-transparent border-gray-300 dark:border-gray-700' :
|
||||
'border-brand-500 bg-brand-500'"
|
||||
class="mr-3 flex h-5 w-5 items-center justify-center rounded-full border-[1.25px]">
|
||||
<span class="h-2 w-2 rounded-full"
|
||||
:class="checkboxToggle ? 'bg-white' : 'bg-white dark:bg-[#171f2e]'"></span>
|
||||
</div>
|
||||
</div>
|
||||
Disabled Secondary
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,36 @@
|
||||
<x-common.component-card title="Select Inputs">
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Select Input
|
||||
</label>
|
||||
<div x-data="{ isOptionSelected: false }" class="relative z-20 bg-transparent">
|
||||
<select
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pr-11 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30"
|
||||
:class="isOptionSelected && 'text-gray-800 dark:text-white/90'" @change="isOptionSelected = true">
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Select Option
|
||||
</option>
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Marketing
|
||||
</option>
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Template
|
||||
</option>
|
||||
<option value="" class="text-gray-700 dark:bg-gray-900 dark:text-gray-400">
|
||||
Development
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
class="pointer-events-none absolute top-1/2 right-4 z-30 -translate-y-1/2 text-gray-700 dark:text-gray-400">
|
||||
<svg class="stroke-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.79175 7.396L10.0001 12.6043L15.2084 7.396" stroke="" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- multiple select --}}
|
||||
<x-form.select.multiple-select/>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,31 @@
|
||||
<x-common.component-card title="Textarea input fields">
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Description
|
||||
</label>
|
||||
<textarea placeholder="Enter a description..." type="text" rows="6"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-300 dark:text-white/15">
|
||||
Description
|
||||
</label>
|
||||
<textarea placeholder="Enter a description..." type="text" rows="6" disabled
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:shadow-focus-ring dark:focus:border-brand-800 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-0 focus:outline-hidden disabled:border-gray-100 disabled:bg-gray-50 disabled:placeholder:text-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:disabled:border-gray-800 dark:disabled:bg-white/[0.03] dark:disabled:placeholder:text-white/15"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Description
|
||||
</label>
|
||||
<textarea placeholder="Enter a description..." type="text" rows="6"
|
||||
class="dark:bg-dark-900 border-error-300 shadow-theme-xs focus:border-error-300 focus:ring-error-500/10 dark:border-error-700 dark:focus:border-error-800 w-full rounded-lg border bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30"></textarea>
|
||||
<p class="text-theme-xs text-error-500">
|
||||
Please enter a message in the textarea.
|
||||
</p>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@@ -0,0 +1,110 @@
|
||||
<x-common.component-card title="Toggle switch input">
|
||||
<!-- Elements -->
|
||||
<div class="mb-6 flex flex-wrap items-center gap-6 sm:gap-8">
|
||||
<div x-data="{ switcherToggle: false }">
|
||||
<label for="toggle1"
|
||||
class="flex cursor-pointer items-center gap-3 text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="toggle1" class="sr-only" @change="switcherToggle = !switcherToggle" />
|
||||
<div class="block h-6 w-11 rounded-full"
|
||||
:class="switcherToggle ? 'bg-brand-500 dark:bg-brand-500' : 'bg-gray-200 dark:bg-white/10'">
|
||||
</div>
|
||||
<div :class="switcherToggle ? 'translate-x-full' : 'translate-x-0'"
|
||||
class="shadow-theme-sm absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white duration-300 ease-linear">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Default
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ switcherToggle: true }">
|
||||
<label for="toggle2"
|
||||
class="flex cursor-pointer items-center gap-3 text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="toggle2" class="sr-only" @change="switcherToggle = !switcherToggle" />
|
||||
<div class="block h-6 w-11 rounded-full"
|
||||
:class="switcherToggle ? 'bg-brand-500 dark:bg-brand-500' : 'bg-gray-200 dark:bg-white/10'">
|
||||
</div>
|
||||
<div :class="switcherToggle ? 'translate-x-full' : 'translate-x-0'"
|
||||
class="shadow-theme-sm absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white duration-300 ease-linear">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Checked
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ switcherToggle: false }">
|
||||
<label for="toggle3"
|
||||
class="flex cursor-pointer items-center gap-3 text-sm font-medium text-gray-400 select-none">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="toggle3" class="sr-only" @change="switcherToggle = !switcherToggle"
|
||||
disabled />
|
||||
<div class="block h-6 w-11 rounded-full"
|
||||
:class="switcherToggle ? 'bg-brand-500 dark:bg-brand-500' : 'bg-gray-100 dark:bg-gray-800'">
|
||||
</div>
|
||||
<div :class="switcherToggle ? 'translate-x-full' : 'translate-x-0'"
|
||||
class="shadow-theme-sm absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-gray-50 duration-300 ease-linear">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Elements -->
|
||||
<div class="flex flex-wrap items-center gap-6 sm:gap-8">
|
||||
<div x-data="{ switcherToggle: false }">
|
||||
<label for="toggle11"
|
||||
class="flex cursor-pointer items-center gap-3 text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="toggle11" class="sr-only" @change="switcherToggle = !switcherToggle" />
|
||||
<div class="block h-6 w-11 rounded-full"
|
||||
:class="switcherToggle ? 'bg-gray-700 dark:bg-white/10' : 'bg-gray-200 dark:bg-gray-800'"></div>
|
||||
<div :class="switcherToggle ? 'translate-x-full' : 'translate-x-0'"
|
||||
class="shadow-theme-sm absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white duration-300 ease-linear">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Default
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ switcherToggle: true }">
|
||||
<label for="toggle22"
|
||||
class="flex cursor-pointer items-center gap-3 text-sm font-medium text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="toggle22" class="sr-only" @change="switcherToggle = !switcherToggle" />
|
||||
|
||||
<div class="block h-6 w-11 rounded-full"
|
||||
:class="switcherToggle ? 'bg-gray-700 dark:bg-white/10' : 'bg-gray-200 dark:bg-gray-800'"></div>
|
||||
<div :class="switcherToggle ? 'translate-x-full' : 'translate-x-0'"
|
||||
class="shadow-theme-sm absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white duration-300 ease-linear">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Checked
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-data="{ switcherToggle: false }">
|
||||
<label for="toggle33"
|
||||
class="flex cursor-pointer items-center gap-3 text-sm font-medium text-gray-400 select-none">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="toggle33" class="sr-only" @change="switcherToggle = !switcherToggle"
|
||||
disabled />
|
||||
<div class="block h-6 w-11 rounded-full"
|
||||
:class="switcherToggle ? 'bg-gray-700 dark:bg-white/10' : 'bg-gray-100 dark:bg-gray-800'">
|
||||
</div>
|
||||
<div :class="switcherToggle ? 'translate-x-full' : 'translate-x-0'"
|
||||
class="shadow-theme-sm absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-gray-50 duration-300 ease-linear">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
43
resources/views/components/form/input/radio.blade.php
Normal file
43
resources/views/components/form/input/radio.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
@props([
|
||||
'id',
|
||||
'name',
|
||||
'value',
|
||||
'checked' => false,
|
||||
'label',
|
||||
'disabled' => false,
|
||||
])
|
||||
|
||||
<label for="{{ $id }}"
|
||||
@class([
|
||||
'relative flex cursor-pointer select-none items-center gap-3 text-sm font-medium',
|
||||
'text-gray-300 dark:text-gray-600 cursor-not-allowed' => $disabled,
|
||||
'text-gray-700 dark:text-gray-400' => !$disabled,
|
||||
$attributes->get('class'),
|
||||
])>
|
||||
|
||||
<input
|
||||
id="{{ $id }}"
|
||||
name="{{ $name }}"
|
||||
type="radio"
|
||||
value="{{ $value }}"
|
||||
{{ $checked ? 'checked' : '' }}
|
||||
{{ $disabled ? 'disabled' : '' }}
|
||||
class="sr-only"
|
||||
{{ $attributes->except(['class', 'label']) }}
|
||||
/>
|
||||
|
||||
<span @class([
|
||||
'flex h-5 w-5 items-center justify-center rounded-full border-[1.25px]',
|
||||
'border-brand-500 bg-brand-500' => $checked && !$disabled,
|
||||
'bg-transparent border-gray-300 dark:border-gray-700' => !$checked && !$disabled,
|
||||
'bg-gray-100 dark:bg-gray-700 border-gray-200 dark:border-gray-700' => $disabled,
|
||||
])>
|
||||
<span @class([
|
||||
'h-2 w-2 rounded-full bg-white',
|
||||
'block' => $checked,
|
||||
'hidden' => !$checked,
|
||||
])></span>
|
||||
</span>
|
||||
|
||||
{{ $label }}
|
||||
</label>
|
||||
@@ -0,0 +1,80 @@
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Multiple Select Options
|
||||
</label>
|
||||
|
||||
<div x-data="{
|
||||
open: false,
|
||||
selected: [1, 3],
|
||||
options: [
|
||||
{ id: 1, name: 'Option 1' },
|
||||
{ id: 2, name: 'Option 2' },
|
||||
{ id: 3, name: 'Option 3' },
|
||||
{ id: 4, name: 'Option 4' },
|
||||
{ id: 5, name: 'Option 5' }
|
||||
],
|
||||
toggleOption(id) {
|
||||
if (this.selected.includes(id)) {
|
||||
this.selected = this.selected.filter(i => i !== id);
|
||||
} else {
|
||||
this.selected.push(id);
|
||||
}
|
||||
},
|
||||
isSelected(id) {
|
||||
return this.selected.includes(id);
|
||||
}
|
||||
}" class="relative" @click.away="open = false">
|
||||
<!-- Hidden input for form submission -->
|
||||
<input type="hidden" name="selected_options" :value="selected.join(',')" />
|
||||
|
||||
<!-- Select Input with Selected Tags -->
|
||||
<div @click="open = !open"
|
||||
class="shadow-theme-xs flex min-h-11 cursor-pointer gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2 transition dark:border-gray-700 dark:bg-gray-900">
|
||||
<!-- Selected Items as Tags -->
|
||||
<div class="flex flex-1 flex-wrap items-center gap-2">
|
||||
<template x-for="id in selected" :key="id">
|
||||
<div
|
||||
class="group flex items-center justify-center rounded-full border-[0.7px] border-transparent bg-gray-100 py-1 pr-2 pl-2.5 text-sm text-gray-800 hover:border-gray-200 dark:bg-gray-800 dark:text-white/90 dark:hover:border-gray-800">
|
||||
<span x-text="options.find(o => o.id === id).name"></span>
|
||||
<button type="button" @click.stop="toggleOption(id)"
|
||||
class="ml-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<svg class="fill-current" role="button" width="14" height="14" viewBox="0 0 14 14"
|
||||
fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M3.40717 4.46881C3.11428 4.17591 3.11428 3.70104 3.40717 3.40815C3.70006 3.11525 4.17494 3.11525 4.46783 3.40815L6.99943 5.93975L9.53095 3.40822C9.82385 3.11533 10.2987 3.11533 10.5916 3.40822C10.8845 3.70112 10.8845 4.17599 10.5916 4.46888L8.06009 7.00041L10.5916 9.53193C10.8845 9.82482 10.8845 10.2997 10.5916 10.5926C10.2987 10.8855 9.82385 10.8855 9.53095 10.5926L6.99943 8.06107L4.46783 10.5927C4.17494 10.8856 3.70006 10.8856 3.40717 10.5927C3.11428 10.2998 3.11428 9.8249 3.40717 9.53201L5.93877 7.00041L3.40717 4.46881Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Show placeholder when nothing is selected -->
|
||||
<span x-show="selected.length === 0" class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Select options...
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Arrow -->
|
||||
<div class="flex items-start pt-1.5">
|
||||
<svg class="h-5 w-5 shrink-0 text-gray-500 transition-transform dark:text-gray-400"
|
||||
:class="open ? 'rotate-180' : ''" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Options List -->
|
||||
<div x-show="open"
|
||||
class="absolute z-50 mt-1 w-full overflow-hidden rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900"
|
||||
style="max-height: 16rem">
|
||||
<div class="overflow-y-auto" style="max-height: 16rem">
|
||||
<template x-for="option in options" :key="option.id">
|
||||
<div @click="toggleOption(option.id)"
|
||||
class="cursor-pointer border-b border-gray-200 px-4 py-3 text-sm transition last:border-b-0 dark:border-gray-800">
|
||||
<span class="text-gray-800 dark:text-white/90" x-text="option.name"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,222 @@
|
||||
{{-- Notification Dropdown Component --}}
|
||||
<div class="relative" x-data="{
|
||||
dropdownOpen: false,
|
||||
notifying: true,
|
||||
toggleDropdown() {
|
||||
this.dropdownOpen = !this.dropdownOpen;
|
||||
this.notifying = false;
|
||||
},
|
||||
closeDropdown() {
|
||||
this.dropdownOpen = false;
|
||||
},
|
||||
handleItemClick() {
|
||||
console.log('Notification item clicked');
|
||||
this.closeDropdown();
|
||||
},
|
||||
handleViewAllClick() {
|
||||
console.log('View All Notifications clicked');
|
||||
this.closeDropdown();
|
||||
}
|
||||
}" @click.away="closeDropdown()">
|
||||
<!-- Notification Button -->
|
||||
<button
|
||||
class="relative flex items-center justify-center text-gray-500 transition-colors bg-white border border-gray-200 rounded-full hover:text-dark-900 h-11 w-11 hover:bg-gray-100 hover:text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
|
||||
@click="toggleDropdown()"
|
||||
type="button"
|
||||
>
|
||||
<!-- Notification Badge -->
|
||||
<span
|
||||
x-show="notifying"
|
||||
class="absolute right-0 top-0.5 z-1 h-2 w-2 rounded-full bg-orange-400"
|
||||
>
|
||||
<span
|
||||
class="absolute inline-flex w-full h-full bg-orange-400 rounded-full opacity-75 -z-1 animate-ping"
|
||||
></span>
|
||||
</span>
|
||||
|
||||
<!-- Bell Icon -->
|
||||
<svg
|
||||
class="fill-current"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10.75 2.29248C10.75 1.87827 10.4143 1.54248 10 1.54248C9.58583 1.54248 9.25004 1.87827 9.25004 2.29248V2.83613C6.08266 3.20733 3.62504 5.9004 3.62504 9.16748V14.4591H3.33337C2.91916 14.4591 2.58337 14.7949 2.58337 15.2091C2.58337 15.6234 2.91916 15.9591 3.33337 15.9591H4.37504H15.625H16.6667C17.0809 15.9591 17.4167 15.6234 17.4167 15.2091C17.4167 14.7949 17.0809 14.4591 16.6667 14.4591H16.375V9.16748C16.375 5.9004 13.9174 3.20733 10.75 2.83613V2.29248ZM14.875 14.4591V9.16748C14.875 6.47509 12.6924 4.29248 10 4.29248C7.30765 4.29248 5.12504 6.47509 5.12504 9.16748V14.4591H14.875ZM8.00004 17.7085C8.00004 18.1228 8.33583 18.4585 8.75004 18.4585H11.25C11.6643 18.4585 12 18.1228 12 17.7085C12 17.2943 11.6643 16.9585 11.25 16.9585H8.75004C8.33583 16.9585 8.00004 17.2943 8.00004 17.7085Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Dropdown Start -->
|
||||
<div
|
||||
x-show="dropdownOpen"
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="transform opacity-0 scale-95"
|
||||
x-transition:enter-end="transform opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="transform opacity-100 scale-100"
|
||||
x-transition:leave-end="transform opacity-0 scale-95"
|
||||
class="absolute -right-[240px] mt-[17px] flex h-[480px] w-[350px] flex-col rounded-2xl border border-gray-200 bg-white p-3 shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark sm:w-[361px] lg:right-0"
|
||||
style="display: none;"
|
||||
>
|
||||
<!-- Dropdown Header -->
|
||||
<div class="flex items-center justify-between pb-3 mb-3 border-b border-gray-100 dark:border-gray-800">
|
||||
<h5 class="text-lg font-semibold text-gray-800 dark:text-white/90">Notification</h5>
|
||||
|
||||
<button @click="closeDropdown()" class="text-gray-500 dark:text-gray-400" type="button">
|
||||
<svg
|
||||
class="fill-current"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Notification List -->
|
||||
<ul class="flex flex-col h-auto overflow-y-auto custom-scrollbar">
|
||||
@php
|
||||
$notifications = [
|
||||
[
|
||||
'id' => 1,
|
||||
'userName' => 'Terry Franci',
|
||||
'userImage' => '/images/user/user-02.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - Nganter App',
|
||||
'type' => 'Project',
|
||||
'time' => '5 min ago',
|
||||
'status' => 'online',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'userName' => 'Alex Johnson',
|
||||
'userImage' => '/images/user/user-03.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - Nganter App',
|
||||
'type' => 'Project',
|
||||
'time' => '10 min ago',
|
||||
'status' => 'offline',
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'userName' => 'Sarah Williams',
|
||||
'userImage' => '/images/user/user-04.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - Dashboard UI',
|
||||
'type' => 'Project',
|
||||
'time' => '15 min ago',
|
||||
'status' => 'online',
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'userName' => 'Mike Brown',
|
||||
'userImage' => '/images/user/user-05.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - E-commerce',
|
||||
'type' => 'Project',
|
||||
'time' => '20 min ago',
|
||||
'status' => 'online',
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
'userName' => 'Emma Davis',
|
||||
'userImage' => '/images/user/user-06.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - Mobile App',
|
||||
'type' => 'Project',
|
||||
'time' => '25 min ago',
|
||||
'status' => 'offline',
|
||||
],
|
||||
[
|
||||
'id' => 6,
|
||||
'userName' => 'John Smith',
|
||||
'userImage' => '/images/user/user-07.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - Landing Page',
|
||||
'type' => 'Project',
|
||||
'time' => '30 min ago',
|
||||
'status' => 'online',
|
||||
],
|
||||
[
|
||||
'id' => 7,
|
||||
'userName' => 'Lisa Anderson',
|
||||
'userImage' => '/images/user/user-08.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - Blog System',
|
||||
'type' => 'Project',
|
||||
'time' => '35 min ago',
|
||||
'status' => 'online',
|
||||
],
|
||||
[
|
||||
'id' => 8,
|
||||
'userName' => 'David Wilson',
|
||||
'userImage' => '/images/user/user-09.jpg',
|
||||
'action' => 'requests permission to change',
|
||||
'project' => 'Project - CRM Dashboard',
|
||||
'type' => 'Project',
|
||||
'time' => '40 min ago',
|
||||
'status' => 'online',
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
@foreach ($notifications as $notification)
|
||||
<li @click="handleItemClick()">
|
||||
<a
|
||||
class="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
||||
href="#"
|
||||
>
|
||||
<span class="relative block w-full h-10 rounded-full z-1 max-w-10">
|
||||
<img src="{{ $notification['userImage'] }}" alt="User" class="overflow-hidden rounded-full" />
|
||||
<span
|
||||
class="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white dark:border-gray-900 {{ $notification['status'] === 'online' ? 'bg-success-500' : 'bg-error-500' }}"
|
||||
></span>
|
||||
</span>
|
||||
|
||||
<span class="block">
|
||||
<span class="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400">
|
||||
<span class="font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $notification['userName'] }}
|
||||
</span>
|
||||
{{ $notification['action'] }}
|
||||
<span class="font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $notification['project'] }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
<span>{{ $notification['type'] }}</span>
|
||||
<span class="w-1 h-1 bg-gray-400 rounded-full"></span>
|
||||
<span>{{ $notification['time'] }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<!-- View All Button -->
|
||||
<a
|
||||
href="#"
|
||||
class="mt-3 flex justify-center rounded-lg border border-gray-300 bg-white p-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200"
|
||||
@click.prevent="handleViewAllClick()"
|
||||
>
|
||||
View All Notification
|
||||
</a>
|
||||
</div>
|
||||
<!-- Dropdown End -->
|
||||
</div>
|
||||
129
resources/views/components/header/user-dropdown.blade.php
Normal file
129
resources/views/components/header/user-dropdown.blade.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<div class="relative" x-data="{
|
||||
dropdownOpen: false,
|
||||
toggleDropdown() {
|
||||
this.dropdownOpen = !this.dropdownOpen;
|
||||
},
|
||||
closeDropdown() {
|
||||
this.dropdownOpen = false;
|
||||
}
|
||||
}" @click.away="closeDropdown()">
|
||||
<!-- User Button -->
|
||||
<button
|
||||
class="flex items-center text-gray-700 dark:text-gray-400"
|
||||
@click.prevent="toggleDropdown()"
|
||||
type="button"
|
||||
>
|
||||
<span class="mr-3 overflow-hidden rounded-full h-11 w-11">
|
||||
<img src="{{ Auth::user()->avatar ? asset('storage/' . Auth::user()->avatar) : asset('images/user/owner.jpg') }}" alt="User" class="h-full w-full object-cover" />
|
||||
</span>
|
||||
|
||||
<span class="block mr-1 font-medium text-theme-sm">{{ Auth::user()->name ?? 'User' }}</span>
|
||||
|
||||
<!-- Chevron Icon -->
|
||||
<svg
|
||||
class="w-5 h-5 transition-transform duration-200"
|
||||
:class="{ 'rotate-180': dropdownOpen }"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Dropdown Start -->
|
||||
<div
|
||||
x-show="dropdownOpen"
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="transform opacity-0 scale-95"
|
||||
x-transition:enter-end="transform opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="transform opacity-100 scale-100"
|
||||
x-transition:leave-end="transform opacity-0 scale-95"
|
||||
class="absolute right-0 mt-[17px] flex w-[260px] flex-col rounded-2xl border border-gray-200 bg-white p-3 shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark z-50"
|
||||
style="display: none;"
|
||||
>
|
||||
<!-- User Info -->
|
||||
<div>
|
||||
<span class="block font-medium text-gray-700 text-theme-sm dark:text-gray-400">{{ Auth::user()->name ?? 'User' }}</span>
|
||||
<span class="mt-0.5 block text-theme-xs text-gray-500 dark:text-gray-400">{{ Auth::user()->email ?? '' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Menu Items -->
|
||||
<ul class="flex flex-col gap-1 pt-4 pb-3 border-b border-gray-200 dark:border-gray-800">
|
||||
@php
|
||||
$menuItems = [
|
||||
[
|
||||
'text' => 'Edit profile',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 14.1526 4.3002 16.1184 5.61936 17.616C6.17279 15.3096 8.24852 13.5955 10.7246 13.5955H13.2746C15.7509 13.5955 17.8268 15.31 18.38 17.6167C19.6996 16.119 20.5 14.153 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5ZM17.0246 18.8566V18.8455C17.0246 16.7744 15.3457 15.0955 13.2746 15.0955H10.7246C8.65354 15.0955 6.97461 16.7744 6.97461 18.8455V18.856C8.38223 19.8895 10.1198 20.5 12 20.5C13.8798 20.5 15.6171 19.8898 17.0246 18.8566ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM11.9991 7.25C10.8847 7.25 9.98126 8.15342 9.98126 9.26784C9.98126 10.3823 10.8847 11.2857 11.9991 11.2857C13.1135 11.2857 14.0169 10.3823 14.0169 9.26784C14.0169 8.15342 13.1135 7.25 11.9991 7.25ZM8.48126 9.26784C8.48126 7.32499 10.0563 5.75 11.9991 5.75C13.9419 5.75 15.5169 7.32499 15.5169 9.26784C15.5169 11.2107 13.9419 12.7857 11.9991 12.7857C10.0563 12.7857 8.48126 11.2107 8.48126 9.26784Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>',
|
||||
'route_name' => 'profile',
|
||||
],
|
||||
[
|
||||
'text' => 'Account settings',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10.4858 3.5L13.5182 3.5C13.9233 3.5 14.2518 3.82851 14.2518 4.23377C14.2518 5.9529 16.1129 7.02795 17.602 6.1682C17.9528 5.96567 18.4014 6.08586 18.6039 6.43667L20.1203 9.0631C20.3229 9.41407 20.2027 9.86286 19.8517 10.0655C18.3625 10.9253 18.3625 13.0747 19.8517 13.9345C20.2026 14.1372 20.3229 14.5859 20.1203 14.9369L18.6039 17.5634C18.4013 17.9142 17.9528 18.0344 17.602 17.8318C16.1129 16.9721 14.2518 18.0471 14.2518 19.7663C14.2518 20.1715 13.9233 20.5 13.5182 20.5H10.4858C10.0804 20.5 9.75182 20.1714 9.75182 19.766C9.75182 18.0461 7.88983 16.9717 6.40067 17.8314C6.04945 18.0342 5.60037 17.9139 5.39767 17.5628L3.88167 14.937C3.67903 14.586 3.79928 14.1372 4.15026 13.9346C5.63949 13.0748 5.63946 10.9253 4.15025 10.0655C3.79926 9.86282 3.67901 9.41401 3.88165 9.06303L5.39764 6.43725C5.60034 6.08617 6.04943 5.96581 6.40065 6.16858C7.88982 7.02836 9.75182 5.9539 9.75182 4.23399C9.75182 3.82862 10.0804 3.5 10.4858 3.5ZM13.5182 2L10.4858 2C9.25201 2 8.25182 3.00019 8.25182 4.23399C8.25182 4.79884 7.64013 5.15215 7.15065 4.86955C6.08213 4.25263 4.71559 4.61859 4.0986 5.68725L2.58261 8.31303C1.96575 9.38146 2.33183 10.7477 3.40025 11.3645C3.88948 11.647 3.88947 12.3531 3.40026 12.6355C2.33184 13.2524 1.96578 14.6186 2.58263 15.687L4.09863 18.3128C4.71562 19.3814 6.08215 19.7474 7.15067 19.1305C7.64015 18.8479 8.25182 19.2012 8.25182 19.766C8.25182 20.9998 9.25201 22 10.4858 22H13.5182C14.7519 22 15.7518 20.9998 15.7518 19.7663C15.7518 19.2015 16.3632 18.8487 16.852 19.1309C17.9202 19.7476 19.2862 19.3816 19.9029 18.3134L21.4193 15.6869C22.0361 14.6185 21.6701 13.2523 20.6017 12.6355C20.1125 12.3531 20.1125 11.647 20.6017 11.3645C21.6701 10.7477 22.0362 9.38152 21.4193 8.3131L19.903 5.68667C19.2862 4.61842 17.9202 4.25241 16.852 4.86917C16.3632 5.15138 15.7518 4.79856 15.7518 4.23377C15.7518 3.00024 14.7519 2 13.5182 2ZM9.6659 11.9999C9.6659 10.7103 10.7113 9.66493 12.0009 9.66493C13.2905 9.66493 14.3359 10.7103 14.3359 11.9999C14.3359 13.2895 13.2905 14.3349 12.0009 14.3349C10.7113 14.3349 9.6659 13.2895 9.6659 11.9999ZM12.0009 8.16493C9.88289 8.16493 8.1659 9.88191 8.1659 11.9999C8.1659 14.1179 9.88289 15.8349 12.0009 15.8349C14.1189 15.8349 15.8359 14.1179 15.8359 11.9999C15.8359 9.88191 14.1189 8.16493 12.0009 8.16493Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>',
|
||||
'route_name' => 'settings',
|
||||
],
|
||||
[
|
||||
'text' => 'Support',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.5 12C3.5 7.30558 7.30558 3.5 12 3.5C16.6944 3.5 20.5 7.30558 20.5 12C20.5 16.6944 16.6944 20.5 12 20.5C7.30558 20.5 3.5 16.6944 3.5 12ZM12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM11.0991 7.52507C11.0991 8.02213 11.5021 8.42507 11.9991 8.42507H12.0001C12.4972 8.42507 12.9001 8.02213 12.9001 7.52507C12.9001 7.02802 12.4972 6.62507 12.0001 6.62507H11.9991C11.5021 6.62507 11.0991 7.02802 11.0991 7.52507ZM12.0001 17.3714C11.5859 17.3714 11.2501 17.0356 11.2501 16.6214V10.9449C11.2501 10.5307 11.5859 10.1949 12.0001 10.1949C12.4143 10.1949 12.7501 10.5307 12.7501 10.9449V16.6214C12.7501 17.0356 12.4143 17.3714 12.0001 17.3714Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>',
|
||||
'route_name' => 'profile'
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
@foreach ($menuItems as $item)
|
||||
<li>
|
||||
<a
|
||||
href="{{ isset($item['route_name']) && Route::has($item['route_name']) ? route($item['route_name']) : '#' }}"
|
||||
{{-- href="{{ $item['path'] }}" --}}
|
||||
class="flex items-center gap-3 px-3 py-2 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
||||
>
|
||||
<span class="text-gray-500 group-hover:text-gray-700 dark:group-hover:text-gray-300">
|
||||
{!! $item['icon'] !!}
|
||||
</span>
|
||||
{{ $item['text'] }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<!-- Sign Out -->
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button
|
||||
type="submit"
|
||||
class="flex items-center w-full gap-3 px-3 py-2 mt-3 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
||||
@click="closeDropdown()"
|
||||
>
|
||||
<span class="text-gray-500 group-hover:text-gray-700 dark:group-hover:text-gray-300">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
|
||||
</svg>
|
||||
</span>
|
||||
Sign out
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Dropdown End -->
|
||||
</div>
|
||||
56
resources/views/components/profile/address-card.blade.php
Normal file
56
resources/views/components/profile/address-card.blade.php
Normal file
@@ -0,0 +1,56 @@
|
||||
@props(['user'])
|
||||
|
||||
<div x-data="{
|
||||
showAddressModal: false,
|
||||
saveProfile() {
|
||||
this.$refs.addressForm.submit();
|
||||
}
|
||||
}">
|
||||
<div class="p-5 border border-gray-200 rounded-2xl dark:border-gray-800 lg:p-6">
|
||||
<div class="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-6">Address</h4>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-7 2xl:gap-x-32">
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">Country</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->country ?? 'Not set' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">City/State</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $user->city_state ?? 'Not set' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
||||
Postal Code
|
||||
</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->postal_code ?? 'Not set' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">TAX ID</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->tax_id ?? 'Not set' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="showAddressModal = true"
|
||||
class="flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 lg:inline-flex lg:w-auto">
|
||||
<svg class="fill-current" width="18" height="18" viewBox="0 0 18 18" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-profile.address-modal :user="$user" show="showAddressModal" />
|
||||
</div>
|
||||
|
||||
66
resources/views/components/profile/address-modal.blade.php
Normal file
66
resources/views/components/profile/address-modal.blade.php
Normal file
@@ -0,0 +1,66 @@
|
||||
@props(['user', 'show'])
|
||||
|
||||
<x-ui.modal @open-profile-address-modal.window="open = true" x-model="{{ $show }}" :isOpen="false" containerClass="max-w-[700px]">
|
||||
<div
|
||||
class="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Edit Address
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
Update your details to keep your profile up-to-date.
|
||||
</p>
|
||||
</div>
|
||||
<form x-ref="addressForm" action="{{ route('profile.update') }}" method="POST" class="flex flex-col">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<input type="hidden" name="name" value="{{ $user->name }}">
|
||||
<input type="hidden" name="email" value="{{ $user->email }}">
|
||||
<div class="px-2 overflow-y-auto custom-scrollbar">
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Country
|
||||
</label>
|
||||
<input type="text" name="country" value="{{ old('country', $user->country) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
City/State
|
||||
</label>
|
||||
<input type="text" name="city_state" value="{{ old('city_state', $user->city_state) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Postal Code
|
||||
</label>
|
||||
<input type="text" name="postal_code" value="{{ old('postal_code', $user->postal_code) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
TAX ID
|
||||
</label>
|
||||
<input type="text" name="tax_id" value="{{ old('tax_id', $user->tax_id) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button"
|
||||
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto">
|
||||
Close
|
||||
</button>
|
||||
<button @click="saveProfile" type="button"
|
||||
class="flex w-full justify-center rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
120
resources/views/components/profile/avatar-modal.blade.php
Normal file
120
resources/views/components/profile/avatar-modal.blade.php
Normal file
@@ -0,0 +1,120 @@
|
||||
@props(['user', 'show'])
|
||||
|
||||
<x-ui.modal
|
||||
@open-avatar-modal.window="open = true"
|
||||
x-model="{{ $show }}"
|
||||
:isOpen="false"
|
||||
containerClass="max-w-[600px]">
|
||||
<div
|
||||
x-data="{
|
||||
cropper: null,
|
||||
imageSrc: null,
|
||||
isDragging: false,
|
||||
initCropper() {
|
||||
if (this.cropper) {
|
||||
this.cropper.destroy();
|
||||
}
|
||||
const image = this.$refs.cropImage;
|
||||
this.cropper = new Cropper(image, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
dragMode: 'move',
|
||||
autoCropArea: 0.8,
|
||||
restore: false,
|
||||
guides: true,
|
||||
center: true,
|
||||
highlight: false,
|
||||
cropBoxMovable: true,
|
||||
cropBoxResizable: true,
|
||||
toggleDragModeOnDblclick: false,
|
||||
});
|
||||
},
|
||||
handleFile(file) {
|
||||
if (!file || !file.type.startsWith('image/')) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.imageSrc = e.target.result;
|
||||
this.$nextTick(() => {
|
||||
this.initCropper();
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
},
|
||||
saveAvatar() {
|
||||
if (!this.cropper) return;
|
||||
const canvas = this.cropper.getCroppedCanvas({
|
||||
width: 400,
|
||||
height: 400,
|
||||
});
|
||||
canvas.toBlob((blob) => {
|
||||
const formData = new FormData();
|
||||
formData.append('avatar', blob, 'avatar.png');
|
||||
formData.append('_token', '{{ csrf_token() }}');
|
||||
formData.append('_method', 'PATCH');
|
||||
|
||||
fetch('{{ route('profile.update') }}', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
}).then(response => {
|
||||
if (response.ok) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Failed to upload image');
|
||||
}
|
||||
});
|
||||
}, 'image/png');
|
||||
}
|
||||
}"
|
||||
class="relative w-full overflow-hidden rounded-3xl bg-white p-6 dark:bg-gray-900 lg:p-10">
|
||||
<div class="mb-6">
|
||||
<h4 class="mb-2 text-xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Update Profile Picture
|
||||
</h4>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Upload a new photo and crop it to fit.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Drag & Drop Area -->
|
||||
<div x-show="!imageSrc"
|
||||
@dragover.prevent="isDragging = true"
|
||||
@dragleave.prevent="isDragging = false"
|
||||
@drop.prevent="isDragging = false; handleFile($event.dataTransfer.files[0])"
|
||||
:class="isDragging ? 'border-brand-500 bg-brand-50' : 'border-gray-300 dark:border-gray-700'"
|
||||
class="flex flex-col items-center justify-center rounded-2xl border-2 border-dashed p-10 transition-colors">
|
||||
<svg class="mb-4 text-gray-400" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="17 8 12 3 7 8"></polyline>
|
||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
<p class="mb-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
Drag and drop your image here, or
|
||||
</p>
|
||||
<button @click="$refs.fileInput.click()" class="text-brand-500 hover:text-brand-600 text-sm font-medium">
|
||||
browse files
|
||||
</button>
|
||||
<input type="file" x-ref="fileInput" class="hidden" accept="image/*" @change="handleFile($event.target.files[0])">
|
||||
</div>
|
||||
|
||||
<!-- Cropping Area -->
|
||||
<div x-show="imageSrc" class="relative overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800">
|
||||
<div class="max-h-[400px] w-full">
|
||||
<img x-ref="cropImage" :src="imageSrc" class="max-w-full">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex items-center justify-end gap-3">
|
||||
<button @click="open = false; imageSrc = null; if(cropper) cropper.destroy();" type="button"
|
||||
class="rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03]">
|
||||
Cancel
|
||||
</button>
|
||||
<button x-show="imageSrc" @click="saveAvatar" type="button"
|
||||
class="rounded-lg bg-brand-500 px-6 py-2 text-sm font-medium text-white hover:bg-brand-600">
|
||||
Save Photo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
119
resources/views/components/profile/edit-modal.blade.php
Normal file
119
resources/views/components/profile/edit-modal.blade.php
Normal file
@@ -0,0 +1,119 @@
|
||||
@props(['user', 'show'])
|
||||
|
||||
<x-ui.modal @open-profile-info-modal.window="open = true" x-model="{{ $show }}" :isOpen="false" containerClass="max-w-[700px]">
|
||||
<div
|
||||
class="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
||||
<div class="px-2 pr-14">
|
||||
<h4 class="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Edit Personal Information
|
||||
</h4>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
||||
Update your details to keep your profile up-to-date.
|
||||
</p>
|
||||
</div>
|
||||
<form x-ref="profileForm" action="{{ route('profile.update') }}" method="POST" enctype="multipart/form-data" class="flex flex-col">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<input type="hidden" name="name" value="{{ $user->name }}">
|
||||
<input type="hidden" name="email" value="{{ $user->email }}">
|
||||
<div class="custom-scrollbar h-[458px] overflow-y-auto p-2">
|
||||
<div>
|
||||
<h5 class="mb-5 text-lg font-medium text-gray-800 dark:text-white/90 lg:mb-6">
|
||||
Social Links
|
||||
</h5>
|
||||
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Facebook
|
||||
</label>
|
||||
<input type="text" name="facebook" value="{{ old('facebook', $user->facebook) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
X.com
|
||||
</label>
|
||||
<input type="text" name="x_link" value="{{ old('x_link', $user->x_link) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Linkedin
|
||||
</label>
|
||||
<input type="text" name="linkedin" value="{{ old('linkedin', $user->linkedin) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Instagram
|
||||
</label>
|
||||
<input type="text" name="instagram" value="{{ old('instagram', $user->instagram) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
<h5 class="mb-5 text-lg font-medium text-gray-800 dark:text-white/90 lg:mb-6">
|
||||
Personal Information
|
||||
</h5>
|
||||
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
||||
<div class="col-span-2 lg:col-span-1">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
First Name
|
||||
</label>
|
||||
<input type="text" name="first_name" value="{{ old('first_name', $user->first_name) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 lg:col-span-1">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Last Name
|
||||
</label>
|
||||
<input type="text" name="last_name" value="{{ old('last_name', $user->last_name) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 lg:col-span-1">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email Address
|
||||
</label>
|
||||
<input type="text" name="email" value="{{ old('email', $user->email) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 lg:col-span-1">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Phone
|
||||
</label>
|
||||
<input type="text" name="phone" value="{{ old('phone', $user->phone) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Bio
|
||||
</label>
|
||||
<input type="text" name="bio" value="{{ old('bio', $user->bio) }}"
|
||||
class="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
||||
<button @click="open = false" type="button"
|
||||
class="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto">
|
||||
Close
|
||||
</button>
|
||||
<button @click="saveProfile" type="button"
|
||||
class="flex w-full justify-center rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
@@ -0,0 +1,67 @@
|
||||
@props(['user'])
|
||||
|
||||
<div x-data="{
|
||||
saveProfile() {
|
||||
this.$refs.personalInfoForm.submit();
|
||||
}
|
||||
}">
|
||||
<div class="p-5 mb-6 border border-gray-200 rounded-2xl dark:border-gray-800 lg:p-6">
|
||||
<div class="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-6">
|
||||
Personal Information
|
||||
</h4>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-7 2xl:gap-x-32">
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">First Name</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->first_name ?? 'Not set' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">Last Name</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->last_name ?? 'Not set' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
||||
Email address
|
||||
</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $user->email }}
|
||||
@if($user->hasVerifiedEmail())
|
||||
<span class="inline-flex items-center ml-2 px-2 py-0.5 rounded text-xs font-medium bg-green-50 text-green-700 dark:bg-green-500/15 dark:text-green-500">
|
||||
Verified
|
||||
</span>
|
||||
@else
|
||||
<span class="inline-flex items-center ml-2 px-2 py-0.5 rounded text-xs font-medium bg-red-50 text-red-700 dark:bg-red-500/15 dark:text-red-500">
|
||||
Unverified
|
||||
</span>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">Phone</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->phone ?? 'Not set' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">Bio</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->bio ?? 'Not set' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="edit-button" @click="$dispatch('open-profile-info-modal')">
|
||||
<svg class="fill-current" width="18" height="18" viewBox="0 0 18 18" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
96
resources/views/components/profile/profile-card.blade.php
Normal file
96
resources/views/components/profile/profile-card.blade.php
Normal file
@@ -0,0 +1,96 @@
|
||||
@props(['user'])
|
||||
|
||||
<div x-data="{
|
||||
showProfileModal: false,
|
||||
showAvatarModal: false,
|
||||
saveProfile() {
|
||||
this.$refs.profileForm.submit();
|
||||
}
|
||||
}">
|
||||
<div class="mb-6 rounded-2xl border border-gray-200 p-5 lg:p-6 dark:border-gray-800">
|
||||
<div class="flex flex-col gap-5 xl:flex-row xl:items-center xl:justify-between">
|
||||
<div class="flex w-full flex-col items-center gap-6 xl:flex-row">
|
||||
<div class="group relative h-20 w-20 overflow-hidden rounded-full border border-gray-200 dark:border-gray-800">
|
||||
<img src="{{ $user->avatar ? asset('storage/' . $user->avatar) : asset('images/user/owner.jpg') }}" alt="user" class="h-full w-full object-cover" />
|
||||
<button @click="$dispatch('open-avatar-modal')" class="absolute inset-0 flex items-center justify-center bg-black/40 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<svg class="text-white" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z" fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="order-3 xl:order-2">
|
||||
<h4 class="mb-2 text-center text-lg font-semibold text-gray-800 xl:text-left dark:text-white/90">
|
||||
{{ $user->name }}
|
||||
</h4>
|
||||
<div class="flex flex-col items-center gap-1 text-center xl:flex-row xl:gap-3 xl:text-left">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $user->bio ?? 'No bio available' }}
|
||||
</p>
|
||||
<div class="hidden h-3.5 w-px bg-gray-300 xl:block dark:bg-gray-700"></div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $user->city_state ?? '' }}{{ $user->country ? ', ' . $user->country : '' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-2 flex grow items-center gap-2 xl:order-3 xl:justify-end">
|
||||
<button
|
||||
class="shadow-theme-xs flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M11.6666 11.2503H13.7499L14.5833 7.91699H11.6666V6.25033C11.6666 5.39251 11.6666 4.58366 13.3333 4.58366H14.5833V1.78374C14.3118 1.7477 13.2858 1.66699 12.2023 1.66699C9.94025 1.66699 8.33325 3.04771 8.33325 5.58342V7.91699H5.83325V11.2503H8.33325V18.3337H11.6666V11.2503Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="shadow-theme-xs flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M15.1708 1.875H17.9274L11.9049 8.75833L18.9899 18.125H13.4424L9.09742 12.4442L4.12578 18.125H1.36745L7.80912 10.7625L1.01245 1.875H6.70078L10.6283 7.0675L15.1708 1.875ZM14.2033 16.475H15.7308L5.87078 3.43833H4.23162L14.2033 16.475Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="shadow-theme-xs flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.78381 4.16645C5.78351 4.84504 5.37181 5.45569 4.74286 5.71045C4.11391 5.96521 3.39331 5.81321 2.92083 5.32613C2.44836 4.83904 2.31837 4.11413 2.59216 3.49323C2.86596 2.87233 3.48886 2.47942 4.16715 2.49978C5.06804 2.52682 5.78422 3.26515 5.78381 4.16645ZM5.83381 7.06645H2.50048V17.4998H5.83381V7.06645ZM11.1005 7.06645H7.78381V17.4998H11.0672V12.0248C11.0672 8.97475 15.0422 8.69142 15.0422 12.0248V17.4998H18.3338V10.8914C18.3338 5.74978 12.4505 5.94145 11.0672 8.46642L11.1005 7.06645Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="shadow-theme-xs flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10.8567 1.66699C11.7946 1.66854 12.2698 1.67351 12.6805 1.68573L12.8422 1.69102C13.0291 1.69766 13.2134 1.70599 13.4357 1.71641C14.3224 1.75738 14.9273 1.89766 15.4586 2.10391C16.0078 2.31572 16.4717 2.60183 16.9349 3.06503C17.3974 3.52822 17.6836 3.99349 17.8961 4.54141C18.1016 5.07197 18.2419 5.67753 18.2836 6.56433C18.2935 6.78655 18.3015 6.97088 18.3081 7.15775L18.3133 7.31949C18.3255 7.73011 18.3311 8.20543 18.3328 9.1433L18.3335 9.76463C18.3336 9.84055 18.3336 9.91888 18.3336 9.99972L18.3335 10.2348L18.333 10.8562C18.3314 11.794 18.3265 12.2694 18.3142 12.68L18.3089 12.8417C18.3023 13.0286 18.294 13.213 18.2836 13.4351C18.2426 14.322 18.1016 14.9268 17.8961 15.458C17.6842 16.0074 17.3974 16.4713 16.9349 16.9345C16.4717 17.397 16.0057 17.6831 15.4586 17.8955C14.9273 18.1011 14.3224 18.2414 13.4357 18.2831C13.2134 18.293 13.0291 18.3011 12.8422 18.3076L12.6805 18.3128C12.2698 18.3251 11.7946 18.3306 10.8567 18.3324L10.2353 18.333C10.1594 18.333 10.0811 18.333 10.0002 18.333H9.76516L9.14375 18.3325C8.20591 18.331 7.7306 18.326 7.31997 18.3137L7.15824 18.3085C6.97136 18.3018 6.78703 18.2935 6.56481 18.2831C5.67801 18.2421 5.07384 18.1011 4.5419 17.8955C3.99328 17.6838 3.5287 17.397 3.06551 16.9345C2.60231 16.4713 2.3169 16.0053 2.1044 15.458C1.89815 14.9268 1.75856 14.322 1.7169 13.4351C1.707 13.213 1.69892 13.0286 1.69238 12.8417L1.68714 12.68C1.67495 12.2694 1.66939 11.794 1.66759 10.8562L1.66748 9.1433C1.66903 8.20543 1.67399 7.73011 1.68621 7.31949L1.69151 7.15775C1.69815 6.97088 1.70648 6.78655 1.7169 6.56433C1.75786 5.67683 1.89815 5.07266 2.1044 4.54141C2.3162 3.9928 2.60231 3.52822 3.06551 3.06503C3.5287 2.60183 3.99398 2.31641 4.5419 2.10391C5.07315 1.89766 5.67731 1.75808 6.56481 1.71641C6.78703 1.70652 6.97136 1.69844 7.15824 1.6919L7.31997 1.68666C7.7306 1.67446 8.20591 1.6689 9.14375 1.6671L10.8567 1.66699ZM10.0002 5.83308C7.69781 5.83308 5.83356 7.69935 5.83356 9.99972C5.83356 12.3021 7.69984 14.1664 10.0002 14.1664C12.3027 14.1664 14.1669 12.3001 14.1669 9.99972C14.1669 7.69732 12.3006 5.83308 10.0002 5.83308ZM10.0002 7.49974C11.381 7.49974 12.5002 8.61863 12.5002 9.99972C12.5002 11.3805 11.3813 12.4997 10.0002 12.4997C8.6195 12.4997 7.50023 11.3809 7.50023 9.99972C7.50023 8.61897 8.61908 7.49974 10.0002 7.49974ZM14.3752 4.58308C13.8008 4.58308 13.3336 5.04967 13.3336 5.62403C13.3336 6.19841 13.8002 6.66572 14.3752 6.66572C14.9496 6.66572 15.4169 6.19913 15.4169 5.62403C15.4169 5.04967 14.9488 4.58236 14.3752 4.58308Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="$dispatch('open-profile-info-modal')"
|
||||
class="shadow-theme-xs flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-800 lg:inline-flex lg:w-auto dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="fill-current" width="18" height="18" viewBox="0 0 18 18" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Refactored Modals -->
|
||||
<x-profile.edit-modal :user="$user" show="showProfileModal" />
|
||||
<x-profile.avatar-modal :user="$user" show="showAvatarModal" />
|
||||
|
||||
<!-- Avatar Update Modal handled by x-profile.avatar-modal -->
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
@props(['show', 'deleteConfirmation'])
|
||||
|
||||
<x-ui.modal x-model="{{ $show }}" containerClass="max-w-[450px]">
|
||||
<div class="p-6">
|
||||
<div class="mb-4 flex items-center gap-3 text-error-600">
|
||||
<div class="flex h-10 w-10 items-center justify-center rounded-full bg-error-50 dark:bg-error-500/10">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"></path><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold">Delete Account</h3>
|
||||
</div>
|
||||
<p class="mb-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
This action is <span class="font-bold text-error-600">irreversible</span>. All your data will be permanently removed.
|
||||
To confirm, please type <span class="font-mono font-bold text-gray-800 dark:text-white">Yes I will delete my account</span> below.
|
||||
</p>
|
||||
|
||||
<form action="{{ route('settings.destroy') }}" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<div class="mb-6">
|
||||
<input type="text" name="confirmation" x-model="{{ $deleteConfirmation }}" placeholder="Type the confirmation phrase"
|
||||
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2 text-sm text-gray-800 placeholder:text-gray-400 focus:border-error-300 focus:outline-hidden focus:ring-3 focus:ring-error-500/10 dark:border-gray-700 dark:bg-white/[0.03] dark:text-white/90">
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<button type="button" @click="open = false" class="flex-1 rounded-lg border border-gray-300 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-white/5">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" :disabled="{{ $deleteConfirmation }} !== 'Yes I will delete my account'"
|
||||
class="flex-1 rounded-lg bg-error-500 py-2.5 text-sm font-medium text-white hover:bg-error-600 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
Delete My Account
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
21
resources/views/components/settings/password-modal.blade.php
Normal file
21
resources/views/components/settings/password-modal.blade.php
Normal file
@@ -0,0 +1,21 @@
|
||||
@props(['show'])
|
||||
|
||||
<x-ui.modal x-model="{{ $show }}" containerClass="max-w-[400px]">
|
||||
<div class="p-6 text-center">
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-brand-50 dark:bg-brand-500/10">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-brand-500"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
|
||||
</div>
|
||||
<h3 class="mb-2 text-lg font-semibold text-gray-800 dark:text-white/90">Update Password?</h3>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400">
|
||||
Are you sure you want to change your password? You will need to use the new password for your next login.
|
||||
</p>
|
||||
<div class="flex gap-3">
|
||||
<button @click="open = false" class="flex-1 rounded-lg border border-gray-300 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-white/5">
|
||||
Cancel
|
||||
</button>
|
||||
<button @click="submitPasswordForm()" class="flex-1 rounded-lg bg-brand-500 py-2.5 text-sm font-medium text-white hover:bg-brand-600">
|
||||
Yes, Update
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
24
resources/views/components/settings/social-modal.blade.php
Normal file
24
resources/views/components/settings/social-modal.blade.php
Normal file
@@ -0,0 +1,24 @@
|
||||
@props(['show', 'socialProvider', 'submitAction'])
|
||||
|
||||
<x-ui.modal x-model="{{ $show }}" containerClass="max-w-[400px]">
|
||||
<div class="p-6 text-center">
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-warning-50 dark:bg-warning-500/10">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-warning-500"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
||||
</div>
|
||||
<h3 class="mb-2 text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
<span x-text="socialAction === 'connect' ? 'Connect' : 'Disconnect'"></span> <span x-text="{{ $socialProvider }}"></span>?
|
||||
</h3>
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400">
|
||||
Are you sure you want to <span x-text="socialAction"></span> your <span x-text="{{ $socialProvider }}"></span> account?
|
||||
</p>
|
||||
<div class="flex gap-3">
|
||||
<button @click="open = false" class="flex-1 rounded-lg border border-gray-300 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-white/5">
|
||||
Cancel
|
||||
</button>
|
||||
<button @click="{{ $submitAction }}" class="flex-1 rounded-lg py-2.5 text-sm font-medium text-white transition-colors"
|
||||
:class="socialAction === 'connect' ? 'bg-brand-500 hover:bg-brand-600' : 'bg-warning-500 hover:bg-warning-600'">
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
@@ -0,0 +1,197 @@
|
||||
|
||||
@php
|
||||
$orders = [
|
||||
[
|
||||
'product' => 'TailGrids',
|
||||
'category' => 'UI Kit',
|
||||
'countryFlag' => '/images/country/country-01.svg',
|
||||
'country' => 'USA',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$12,499',
|
||||
],
|
||||
[
|
||||
'product' => 'GrayGrids',
|
||||
'category' => 'Templates',
|
||||
'countryFlag' => '/images/country/country-03.svg',
|
||||
'country' => 'UK',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$5,498',
|
||||
],
|
||||
[
|
||||
'product' => 'Uideck',
|
||||
'category' => 'Templates',
|
||||
'countryFlag' => '/images/country/country-04.svg',
|
||||
'country' => 'Canada',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$4,521',
|
||||
],
|
||||
[
|
||||
'product' => 'FormBold',
|
||||
'category' => 'SaaS',
|
||||
'countryFlag' => '/images/country/country-05.svg',
|
||||
'country' => 'Australia',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$13,843',
|
||||
],
|
||||
[
|
||||
'product' => 'NextAdmin',
|
||||
'category' => 'Dashboard',
|
||||
'countryFlag' => '/images/country/country-06.svg',
|
||||
'country' => 'Germany',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$7,523',
|
||||
],
|
||||
[
|
||||
'product' => 'Form Builder',
|
||||
'category' => 'SaaS',
|
||||
'countryFlag' => '/images/country/country-07.svg',
|
||||
'country' => 'France',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$1,377',
|
||||
],
|
||||
[
|
||||
'product' => 'AyroUI',
|
||||
'category' => 'UI Kit',
|
||||
'countryFlag' => '/images/country/country-08.svg',
|
||||
'country' => 'Japan',
|
||||
'cr' => 'Dashboard',
|
||||
'value' => '$599,00',
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-gray-200 bg-white pt-4 dark:border-gray-800 dark:bg-white/[0.03]"
|
||||
>
|
||||
<div class="flex flex-col gap-4 px-6 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">Recent Orders</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200"
|
||||
>
|
||||
<svg
|
||||
class="stroke-current fill-white dark:fill-gray-800"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.29004 5.90393H17.7067"
|
||||
stroke=""
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M17.7075 14.0961H2.29085"
|
||||
stroke=""
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.0826 3.33331C13.5024 3.33331 14.6534 4.48431 14.6534 5.90414C14.6534 7.32398 13.5024 8.47498 12.0826 8.47498C10.6627 8.47498 9.51172 7.32398 9.51172 5.90415C9.51172 4.48432 10.6627 3.33331 12.0826 3.33331Z"
|
||||
fill=""
|
||||
stroke=""
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M7.91745 11.525C6.49762 11.525 5.34662 12.676 5.34662 14.0959C5.34661 15.5157 6.49762 16.6667 7.91745 16.6667C9.33728 16.6667 10.4883 15.5157 10.4883 14.0959C10.4883 12.676 9.33728 11.525 7.91745 11.525Z"
|
||||
fill=""
|
||||
stroke=""
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Filter
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200"
|
||||
>
|
||||
See all
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<table class="min-w-full">
|
||||
<!-- table header start -->
|
||||
<thead>
|
||||
<tr class="border-gray-100 border-y dark:border-white/[0.05]">
|
||||
<th class="px-6 py-3">
|
||||
<div class="flex items-center">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Products</p>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-3">
|
||||
<div class="flex items-center">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Category</p>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-3">
|
||||
<div class="flex items-center col-span-2">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Country</p>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-3">
|
||||
<div class="flex items-center col-span-2">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">CR</p>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-3">
|
||||
<div class="flex items-center col-span-2">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Value</p>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- table header end -->
|
||||
|
||||
<!-- table body start -->
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-white/[0.05]">
|
||||
@foreach ($orders as $order)
|
||||
<tr>
|
||||
<td class="px-6 py-3.5">
|
||||
<div class="flex items-center">
|
||||
<p class="font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
||||
{{ $order['product'] }}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-3.5">
|
||||
<div class="flex items-center">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">{{ $order['category'] }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-3.5">
|
||||
<div class="flex items-center">
|
||||
<div class="w-5 h-5 overflow-hidden rounded-full">
|
||||
<img src="{{ $order['countryFlag'] }}" alt="{{ $order['country'] }}" />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-3.5">
|
||||
<div class="flex items-center">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">
|
||||
{{ $order['cr'] }}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-3.5">
|
||||
<div class="flex items-center">
|
||||
<p class="text-theme-sm text-success-600">{{ $order['value'] }}</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<!-- table body end -->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,143 @@
|
||||
@php
|
||||
$campaigns = [
|
||||
[
|
||||
'creator' => ['name' => 'Wilson Gouse', 'imageUrl' => '/images/user/user-01.jpg'],
|
||||
'brand' => ['name' => 'Brand 1', 'logo' => '/images/brand/brand-01.svg'],
|
||||
'title' => 'Grow your brand by...',
|
||||
'type' => 'Ads campaign',
|
||||
'status' => 'Success',
|
||||
],
|
||||
[
|
||||
'creator' => ['name' => 'Terry Franci', 'imageUrl' => '/images/user/user-02.jpg'],
|
||||
'brand' => ['name' => 'Brand 2', 'logo' => '/images/brand/brand-02.svg'],
|
||||
'title' => 'Make Better Ideas...',
|
||||
'type' => 'Ads campaign',
|
||||
'status' => 'Pending',
|
||||
],
|
||||
[
|
||||
'creator' => ['name' => 'Alena Franci', 'imageUrl' => '/images/user/user-03.jpg'],
|
||||
'brand' => ['name' => 'Brand 3', 'logo' => '/images/brand/brand-03.svg'],
|
||||
'title' => 'Increase your website tra...',
|
||||
'type' => 'Ads campaign',
|
||||
'status' => 'Success',
|
||||
],
|
||||
[
|
||||
'creator' => ['name' => 'Jocelyn Kenter', 'imageUrl' => '/images/user/user-04.jpg'],
|
||||
'brand' => ['name' => 'Brand 4', 'logo' => '/images/brand/brand-04.svg'],
|
||||
'title' => 'Digital Marketing that...',
|
||||
'type' => 'Ads campaign',
|
||||
'status' => 'Failed',
|
||||
],
|
||||
[
|
||||
'creator' => ['name' => 'Brandon Philips', 'imageUrl' => '/images/user/user-05.jpg'],
|
||||
'brand' => ['name' => 'Brand 2', 'logo' => '/images/brand/brand-02.svg'],
|
||||
'title' => 'Self branding',
|
||||
'type' => 'Ads campaign',
|
||||
'status' => 'Success',
|
||||
],
|
||||
[
|
||||
'creator' => ['name' => 'James Lipshutz', 'imageUrl' => '/images/user/user-06.jpg'],
|
||||
'brand' => ['name' => 'Brand 3', 'logo' => '/images/brand/brand-03.svg'],
|
||||
'title' => 'Increase your website tra...',
|
||||
'type' => 'Ads campaign',
|
||||
'status' => 'Success',
|
||||
],
|
||||
];
|
||||
|
||||
function getStatusClass($status) {
|
||||
$baseClasses = 'rounded-full px-2 text-theme-xs font-medium';
|
||||
switch ($status) {
|
||||
case 'Success':
|
||||
return "$baseClasses bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500";
|
||||
case 'Pending':
|
||||
return "$baseClasses bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-orange-400";
|
||||
case 'Failed':
|
||||
return "$baseClasses bg-error-50 text-error-600 dark:bg-error-500/15 dark:text-error-500";
|
||||
default:
|
||||
return $baseClasses;
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pb-3 pt-4 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6"
|
||||
>
|
||||
<div class="flex justify-between gap-2 mb-4 sm:items-center">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">Featured Campaigns</h3>
|
||||
</div>
|
||||
|
||||
<div class="relative"></div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<table class="min-w-full">
|
||||
<!-- table header start -->
|
||||
<thead>
|
||||
<tr class="border-gray-100 border-y dark:border-gray-800">
|
||||
<th class="py-3 font-normal">
|
||||
<div class="flex items-center">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">Creator</p>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 font-normal">
|
||||
<div class="flex items-center">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">Campaign</p>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 font-normal">
|
||||
<div class="flex items-center">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">Status</p>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- table header end -->
|
||||
|
||||
<!-- table body start -->
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
|
||||
@foreach ($campaigns as $campaign)
|
||||
<tr>
|
||||
<td class="py-3">
|
||||
<div class="flex items-center gap-[18px]">
|
||||
<div class="w-10 h-10 overflow-hidden rounded-full">
|
||||
<img src="{{ $campaign['creator']['imageUrl'] }}" alt="{{ $campaign['creator']['name'] }}" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-gray-700 text-theme-sm dark:text-gray-400">
|
||||
{{ $campaign['creator']['name'] }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center w-full gap-5">
|
||||
<div class="w-full max-w-8">
|
||||
<img src="{{ $campaign['brand']['logo'] }}" class="size-8" alt="{{ $campaign['brand']['name'] }}" />
|
||||
</div>
|
||||
<div class="truncate">
|
||||
<p class="mb-0.5 truncate text-theme-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
{{ $campaign['title'] }}
|
||||
</p>
|
||||
<span class="text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
{{ $campaign['type'] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<div class="flex items-center">
|
||||
<span class="{{ getStatusClass($campaign['status']) }}">
|
||||
{{ $campaign['status'] }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<!-- table body end -->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,170 @@
|
||||
<div x-data="{
|
||||
orders: [
|
||||
{
|
||||
id: 1,
|
||||
user: {
|
||||
image: './images/user/user-17.jpg',
|
||||
name: 'Lindsey Curtis',
|
||||
role: 'Web Designer',
|
||||
},
|
||||
projectName: 'Agency Website',
|
||||
team: {
|
||||
images: [
|
||||
'./images/user/user-22.jpg',
|
||||
'./images/user/user-23.jpg',
|
||||
'./images/user/user-24.jpg',
|
||||
],
|
||||
},
|
||||
budget: '3.9K',
|
||||
status: 'Active',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
user: {
|
||||
image: './images/user/user-18.jpg',
|
||||
name: 'Kaiya George',
|
||||
role: 'Project Manager',
|
||||
},
|
||||
projectName: 'Technology',
|
||||
team: {
|
||||
images: [
|
||||
'./images/user/user-25.jpg',
|
||||
'./images/user/user-26.jpg',
|
||||
],
|
||||
},
|
||||
budget: '24.9K',
|
||||
status: 'Pending',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
user: {
|
||||
image: './images/user/user-19.jpg',
|
||||
name: 'Zain Geidt',
|
||||
role: 'Content Writer',
|
||||
},
|
||||
projectName: 'Blog Writing',
|
||||
team: {
|
||||
images: [
|
||||
'./images/user/user-27.jpg',
|
||||
],
|
||||
},
|
||||
budget: '12.7K',
|
||||
status: 'Active',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
user: {
|
||||
image: './images/user/user-20.jpg',
|
||||
name: 'Abram Schleifer',
|
||||
role: 'Digital Marketer',
|
||||
},
|
||||
projectName: 'Social Media',
|
||||
team: {
|
||||
images: [
|
||||
'./images/user/user-28.jpg',
|
||||
'./images/user/user-29.jpg',
|
||||
'./images/user/user-30.jpg',
|
||||
],
|
||||
},
|
||||
budget: '2.8K',
|
||||
status: 'Cancel',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
user: {
|
||||
image: './images/user/user-21.jpg',
|
||||
name: 'Carla George',
|
||||
role: 'Front-end Developer',
|
||||
},
|
||||
projectName: 'Website',
|
||||
team: {
|
||||
images: [
|
||||
'./images/user/user-31.jpg',
|
||||
'./images/user/user-32.jpg',
|
||||
'./images/user/user-33.jpg',
|
||||
],
|
||||
},
|
||||
budget: '4.5K',
|
||||
status: 'Active',
|
||||
},
|
||||
],
|
||||
getStatusClass(status) {
|
||||
const classes = {
|
||||
'Active': 'bg-green-50 text-green-700 dark:bg-green-500/15 dark:text-green-500',
|
||||
'Pending': 'bg-yellow-50 text-yellow-700 dark:bg-yellow-500/15 dark:text-yellow-400',
|
||||
'Cancel': 'bg-red-50 text-red-700 dark:bg-red-500/15 dark:text-red-500',
|
||||
};
|
||||
return classes[status] || '';
|
||||
}
|
||||
}">
|
||||
<div class="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<table class="w-full min-w-[1102px]">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-100 dark:border-gray-800">
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
User
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Project Name
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Team
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Status
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Budget
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="order in orders" :key="order.id">
|
||||
<tr class="border-b border-gray-100 dark:border-gray-800">
|
||||
<td class="px-5 py-4 sm:px-6" colspan="1">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 overflow-hidden rounded-full">
|
||||
<img :src="order.user.image" :alt="order.user.name">
|
||||
</div>
|
||||
<div>
|
||||
<span class="block font-medium text-gray-800 text-theme-sm dark:text-white/90" x-text="order.user.name"></span>
|
||||
<span class="block text-gray-500 text-theme-xs dark:text-gray-400" x-text="order.user.role"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400" x-text="order.projectName"></p>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<div class="flex -space-x-2">
|
||||
<template x-for="(teamImage, index) in order.team.images" :key="index">
|
||||
<div class="w-6 h-6 overflow-hidden border-2 border-white rounded-full dark:border-gray-900">
|
||||
<img :src="teamImage" alt="team member">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<p class="text-theme-xs inline-block rounded-full px-2 py-0.5 font-medium" :class="getStatusClass(order.status)" x-text="order.status"></p>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400" x-text="order.budget"></p>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,313 @@
|
||||
<div x-data="{
|
||||
transactions: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Bought PYPL',
|
||||
image: '/images/brand/brand-08.svg',
|
||||
date: 'Nov 23, 01:00 PM',
|
||||
price: '$2,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Bought AAPL',
|
||||
image: '/images/brand/brand-07.svg',
|
||||
date: 'Nov 23, 01:00 PM',
|
||||
price: '$2,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Pending',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Sell KKST',
|
||||
image: '/images/brand/brand-15.svg',
|
||||
date: 'Nov 23, 01:00 PM',
|
||||
price: '$2,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Bought FB',
|
||||
image: '/images/brand/brand-02.svg',
|
||||
date: 'Nov 23, 01:00 PM',
|
||||
price: '$2,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Sell AMZN',
|
||||
image: '/images/brand/brand-10.svg',
|
||||
date: 'Nov 23, 01:00 PM',
|
||||
price: '$2,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Failed',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Bought MSFT',
|
||||
image: '/images/brand/brand-09.svg',
|
||||
date: 'Nov 22, 01:00 PM',
|
||||
price: '$1,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Bought GOOG',
|
||||
image: '/images/brand/brand-01.svg',
|
||||
date: 'Nov 22, 01:00 PM',
|
||||
price: '$3,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Pending',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Sell TSLA',
|
||||
image: '/images/brand/brand-12.svg',
|
||||
date: 'Nov 22, 01:00 PM',
|
||||
price: '$4,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Bought NVDA',
|
||||
image: '/images/brand/brand-11.svg',
|
||||
date: 'Nov 22, 01:00 PM',
|
||||
price: '$5,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Sell META',
|
||||
image: '/images/brand/brand-03.svg',
|
||||
date: 'Nov 22, 01:00 PM',
|
||||
price: '$6,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Failed',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Bought DIS',
|
||||
image: '/images/brand/brand-04.svg',
|
||||
date: 'Nov 21, 01:00 PM',
|
||||
price: '$7,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Bought NFLX',
|
||||
image: '/images/brand/brand-05.svg',
|
||||
date: 'Nov 21, 01:00 PM',
|
||||
price: '$8,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Pending',
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Sell CRM',
|
||||
image: '/images/brand/brand-06.svg',
|
||||
date: 'Nov 21, 01:00 PM',
|
||||
price: '$9,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Bought TSLA',
|
||||
image: '/images/brand/brand-13.svg',
|
||||
date: 'Nov 21, 01:00 PM',
|
||||
price: '$10,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Success',
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: 'Sell AAPL',
|
||||
image: '/images/brand/brand-14.svg',
|
||||
date: 'Nov 21, 01:00 PM',
|
||||
price: '$11,567.88',
|
||||
category: 'Finance',
|
||||
status: 'Failed',
|
||||
},
|
||||
],
|
||||
itemsPerPage: 5,
|
||||
currentPage: 1,
|
||||
dropdownOpen: null,
|
||||
get totalPages() {
|
||||
return Math.ceil(this.transactions.length / this.itemsPerPage);
|
||||
},
|
||||
get paginatedTransactions() {
|
||||
const start = (this.currentPage - 1) * this.itemsPerPage;
|
||||
const end = start + this.itemsPerPage;
|
||||
return this.transactions.slice(start, end);
|
||||
},
|
||||
get displayedPages() {
|
||||
const range = [];
|
||||
for (let i = 1; i <= this.totalPages; i++) {
|
||||
if (
|
||||
i === 1 ||
|
||||
i === this.totalPages ||
|
||||
(i >= this.currentPage - 1 && i <= this.currentPage + 1)
|
||||
) {
|
||||
range.push(i);
|
||||
} else if (range[range.length - 1] !== '...') {
|
||||
range.push('...');
|
||||
}
|
||||
}
|
||||
return range;
|
||||
},
|
||||
prevPage() {
|
||||
if (this.currentPage > 1) {
|
||||
this.currentPage--;
|
||||
}
|
||||
},
|
||||
nextPage() {
|
||||
if (this.currentPage < this.totalPages) {
|
||||
this.currentPage++;
|
||||
}
|
||||
},
|
||||
goToPage(page) {
|
||||
if (typeof page === 'number' && page >= 1 && page <= this.totalPages) {
|
||||
this.currentPage = page;
|
||||
}
|
||||
},
|
||||
getStatusClass(status) {
|
||||
const classes = {
|
||||
'Success': 'bg-green-50 text-green-600 dark:bg-green-500/15 dark:text-green-500',
|
||||
'Pending': 'bg-yellow-50 text-yellow-600 dark:bg-yellow-500/15 dark:text-orange-400',
|
||||
'Failed': 'bg-red-50 text-red-600 dark:bg-red-500/15 dark:text-red-500',
|
||||
};
|
||||
return classes[status] || '';
|
||||
},
|
||||
toggleDropdown(id) {
|
||||
this.dropdownOpen = this.dropdownOpen === id ? null : id;
|
||||
}
|
||||
}">
|
||||
<div class="rounded-2xl border border-gray-200 bg-white pt-4 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-2 px-5 mb-4 sm:flex-row sm:items-center sm:justify-between sm:px-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">Latest Transactions</h3>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center">
|
||||
<form>
|
||||
<div class="relative">
|
||||
<button type="button" class="absolute -translate-y-1/2 left-4 top-1/2">
|
||||
<svg class="fill-gray-500 dark:fill-gray-400" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.04199 9.37381C3.04199 5.87712 5.87735 3.04218 9.37533 3.04218C12.8733 3.04218 15.7087 5.87712 15.7087 9.37381C15.7087 12.8705 12.8733 15.7055 9.37533 15.7055C5.87735 15.7055 3.04199 12.8705 3.04199 9.37381ZM9.37533 1.54218C5.04926 1.54218 1.54199 5.04835 1.54199 9.37381C1.54199 13.6993 5.04926 17.2055 9.37533 17.2055C11.2676 17.2055 13.0032 16.5346 14.3572 15.4178L17.1773 18.2381C17.4702 18.531 17.945 18.5311 18.2379 18.2382C18.5308 17.9453 18.5309 17.4704 18.238 17.1775L15.4182 14.3575C16.5367 13.0035 17.2087 11.2671 17.2087 9.37381C17.2087 5.04835 13.7014 1.54218 9.37533 1.54218Z" fill=""/>
|
||||
</svg>
|
||||
</button>
|
||||
<input type="text" placeholder="Search..." class="h-[42px] w-full rounded-lg border border-gray-300 bg-transparent py-2.5 pl-[42px] pr-4 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-blue-300 focus:outline-none focus:ring-2 focus:ring-blue-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-blue-800 xl:w-[300px]"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="overflow-hidden">
|
||||
<div class="max-w-full px-5 overflow-x-auto">
|
||||
<table class="min-w-full">
|
||||
<thead>
|
||||
<tr class="border-gray-200 border-y dark:border-gray-700">
|
||||
<th scope="col" class="px-4 py-3 font-normal text-gray-500 text-start text-theme-sm dark:text-gray-400">Name</th>
|
||||
<th scope="col" class="px-4 py-3 font-normal text-gray-500 text-start text-theme-sm dark:text-gray-400">Date</th>
|
||||
<th scope="col" class="px-4 py-3 font-normal text-gray-500 text-start text-theme-sm dark:text-gray-400">Price</th>
|
||||
<th scope="col" class="px-4 py-3 text-xs font-medium tracking-wider text-left text-gray-500 capitalize">Category</th>
|
||||
<th scope="col" class="px-4 py-3 font-normal text-gray-500 text-start text-theme-sm dark:text-gray-400">Status</th>
|
||||
<th scope="col" class="relative px-4 py-3 capitalize">
|
||||
<span class="sr-only">Actions</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<template x-for="transaction in paginatedTransactions" :key="transaction.id">
|
||||
<tr>
|
||||
<td class="py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 w-8 h-8">
|
||||
<img class="w-8 h-8 rounded-full" :src="transaction.image" alt="">
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white" x-text="transaction.name"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400" x-text="transaction.date"></div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400" x-text="transaction.price"></div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400" x-text="transaction.category"></div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap">
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full" :class="getStatusClass(transaction.status)" x-text="transaction.status"></span>
|
||||
</td>
|
||||
<td class="px-4 py-4 text-sm font-medium text-right whitespace-nowrap">
|
||||
<div class="flex justify-center relative">
|
||||
<x-common.table-dropdown>
|
||||
<x-slot name="button">
|
||||
<button type="button" id="options-menu" aria-haspopup="true" aria-expanded="true" class="text-gray-500 dark:text-gray-400'">
|
||||
<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M5.99902 10.245C6.96552 10.245 7.74902 11.0285 7.74902 11.995V12.005C7.74902 12.9715 6.96552 13.755 5.99902 13.755C5.03253 13.755 4.24902 12.9715 4.24902 12.005V11.995C4.24902 11.0285 5.03253 10.245 5.99902 10.245ZM17.999 10.245C18.9655 10.245 19.749 11.0285 19.749 11.995V12.005C19.749 12.9715 18.9655 13.755 17.999 13.755C17.0325 13.755 16.249 12.9715 16.249 12.005V11.995C16.249 11.0285 17.0325 10.245 17.999 10.245ZM13.749 11.995C13.749 11.0285 12.9655 10.245 11.999 10.245C11.0325 10.245 10.249 11.0285 10.249 11.995V12.005C10.249 12.9715 11.0325 13.755 11.999 13.755C12.9655 13.755 13.749 12.9715 13.749 12.005V11.995Z" fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<a href="#" class="flex w-full px-3 py-2 font-medium text-left text-gray-500 rounded-lg text-theme-xs hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300" role="menuitem">
|
||||
View More
|
||||
</a>
|
||||
<a href="#" class="flex w-full px-3 py-2 font-medium text-left text-gray-500 rounded-lg text-theme-xs hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300" role="menuitem">
|
||||
Delete
|
||||
</a>
|
||||
</x-slot>
|
||||
</x-common.table-dropdown>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-white/[0.05]">
|
||||
<div class="flex items-center justify-between">
|
||||
<button @click="prevPage" :disabled="currentPage === 1" :class="currentPage === 1 ? 'opacity-50 cursor-not-allowed' : ''" class="flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-3 py-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 sm:px-3.5">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.58301 9.99868C2.58272 10.1909 2.65588 10.3833 2.80249 10.53L7.79915 15.5301C8.09194 15.8231 8.56682 15.8233 8.85981 15.5305C9.15281 15.2377 9.15297 14.7629 8.86018 14.4699L5.14009 10.7472L16.6675 10.7472C17.0817 10.7472 17.4175 10.4114 17.4175 9.99715C17.4175 9.58294 17.0817 9.24715 16.6675 9.24715L5.14554 9.24715L8.86017 5.53016C9.15297 5.23717 9.15282 4.7623 8.85983 4.4695C8.56684 4.1767 8.09197 4.17685 7.79917 4.46984L2.84167 9.43049C2.68321 9.568 2.58301 9.77087 2.58301 9.99715C2.58301 9.99766 2.58301 9.99817 2.58301 9.99868Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Previous</span>
|
||||
</button>
|
||||
|
||||
<span class="block text-sm font-medium text-gray-700 dark:text-gray-400 sm:hidden">
|
||||
Page <span x-text="currentPage"></span> of <span x-text="totalPages"></span>
|
||||
</span>
|
||||
|
||||
<ul class="hidden items-center gap-0.5 sm:flex">
|
||||
<template x-for="page in displayedPages" :key="page">
|
||||
<li>
|
||||
<button x-show="page !== '...'" @click="goToPage(page)" :class="currentPage === page ? 'bg-blue-500 text-white' : 'text-gray-700 hover:bg-blue-500/[0.08] hover:text-blue-500 dark:text-gray-400 dark:hover:text-blue-500'" class="flex h-10 w-10 items-center justify-center rounded-lg text-theme-sm font-medium" x-text="page"></button>
|
||||
<span x-show="page === '...'" class="flex h-10 w-10 items-center justify-center text-gray-500">...</span>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
|
||||
<button @click="nextPage" :disabled="currentPage === totalPages" :class="currentPage === totalPages ? 'opacity-50 cursor-not-allowed' : ''" class="flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-3 py-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 sm:px-3.5">
|
||||
<span class="hidden sm:inline">Next</span>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4175 9.9986C17.4178 10.1909 17.3446 10.3832 17.198 10.53L12.2013 15.5301C11.9085 15.8231 11.4337 15.8233 11.1407 15.5305C10.8477 15.2377 10.8475 14.7629 11.1403 14.4699L14.8604 10.7472L3.33301 10.7472C2.91879 10.7472 2.58301 10.4114 2.58301 9.99715C2.58301 9.58294 2.91879 9.24715 3.33301 9.24715L14.8549 9.24715L11.1403 5.53016C10.8475 5.23717 10.8477 4.7623 11.1407 4.4695C11.4336 4.1767 11.9085 4.17685 12.2013 4.46984L17.1588 9.43049C17.3173 9.568 17.4175 9.77087 17.4175 9.99715C17.4175 9.99763 17.4175 9.99812 17.4175 9.9986Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,207 @@
|
||||
<div x-data="{
|
||||
tableRowData: [
|
||||
{
|
||||
id: 'DE124321',
|
||||
checked: false,
|
||||
customerName: 'John Doe',
|
||||
customerEmail: 'johndoe@gmail.com',
|
||||
initials: 'JD',
|
||||
avatarBg: 'bg-blue-100',
|
||||
avatarColor: 'text-blue-500',
|
||||
product: 'Software License',
|
||||
value: '$18,50.34',
|
||||
closeDate: '2024-06-15',
|
||||
status: 'Complete',
|
||||
},
|
||||
{
|
||||
id: 'DE124322',
|
||||
checked: false,
|
||||
customerName: 'Kierra Franci',
|
||||
customerEmail: 'kierra@gmail.com',
|
||||
initials: 'KF',
|
||||
avatarBg: 'bg-[#fdf2fa]',
|
||||
avatarColor: 'text-[#dd2590]',
|
||||
product: 'Software License',
|
||||
value: '$18,50.34',
|
||||
closeDate: '2024-06-15',
|
||||
status: 'Complete',
|
||||
},
|
||||
{
|
||||
id: 'DE124323',
|
||||
checked: false,
|
||||
customerName: 'Emerson Workman',
|
||||
customerEmail: 'emerson@gmail.com',
|
||||
initials: 'EW',
|
||||
avatarBg: 'bg-[#f0f9ff]',
|
||||
avatarColor: 'text-[#0086c9]',
|
||||
product: 'Software License',
|
||||
value: '$18,50.34',
|
||||
closeDate: '2024-06-15',
|
||||
status: 'Pending',
|
||||
},
|
||||
{
|
||||
id: 'DE124324',
|
||||
checked: false,
|
||||
customerName: 'Chance Philips',
|
||||
customerEmail: 'chance@gmail.com',
|
||||
initials: 'CP',
|
||||
avatarBg: 'bg-[#fff6ed]',
|
||||
avatarColor: 'text-[#ec4a0a]',
|
||||
product: 'Software License',
|
||||
value: '$18,50.34',
|
||||
closeDate: '2024-06-15',
|
||||
status: 'Complete',
|
||||
},
|
||||
{
|
||||
id: 'DE124325',
|
||||
checked: false,
|
||||
customerName: 'Terry Geidt',
|
||||
customerEmail: 'terry@gmail.com',
|
||||
initials: 'TG',
|
||||
avatarBg: 'bg-green-50',
|
||||
avatarColor: 'text-green-600',
|
||||
product: 'Software License',
|
||||
value: '$18,50.34',
|
||||
closeDate: '2024-06-15',
|
||||
status: 'Complete',
|
||||
},
|
||||
],
|
||||
selectedRows: [],
|
||||
selectAll: false,
|
||||
handleSelectAll() {
|
||||
this.selectAll = !this.selectAll;
|
||||
if (this.selectAll) {
|
||||
this.selectedRows = this.tableRowData.map(row => row.id);
|
||||
} else {
|
||||
this.selectedRows = [];
|
||||
}
|
||||
},
|
||||
handleRowSelect(id) {
|
||||
if (this.selectedRows.includes(id)) {
|
||||
this.selectedRows = this.selectedRows.filter(rowId => rowId !== id);
|
||||
} else {
|
||||
this.selectedRows.push(id);
|
||||
}
|
||||
},
|
||||
getStatusClass(status) {
|
||||
const classes = {
|
||||
'Complete': 'bg-green-50 text-green-700 dark:bg-green-500/15 dark:text-green-500',
|
||||
'Pending': 'bg-yellow-50 text-yellow-700 dark:bg-yellow-500/15 dark:text-yellow-400',
|
||||
'Cancel': 'bg-red-50 text-red-700 dark:bg-red-500/15 dark:text-red-500',
|
||||
};
|
||||
return classes[status] || '';
|
||||
},
|
||||
deleteRow(id) {
|
||||
if (confirm('Are you sure you want to delete this order?')) {
|
||||
this.tableRowData = this.tableRowData.filter(row => row.id !== id);
|
||||
this.selectedRows = this.selectedRows.filter(rowId => rowId !== id);
|
||||
}
|
||||
}
|
||||
}">
|
||||
<div class="overflow-hidden rounded-2xl border border-gray-200 bg-white pt-4 dark:border-white/[0.05] dark:bg-white/[0.03]">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4 px-6 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Recent Orders
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
<svg class="stroke-current fill-white dark:fill-gray-800" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.29004 5.90393H17.7067" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.7075 14.0961H2.29085" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.0826 3.33331C13.5024 3.33331 14.6534 4.48431 14.6534 5.90414C14.6534 7.32398 13.5024 8.47498 12.0826 8.47498C10.6627 8.47498 9.51172 7.32398 9.51172 5.90415C9.51172 4.48432 10.6627 3.33331 12.0826 3.33331Z" fill="" stroke="" stroke-width="1.5"/>
|
||||
<path d="M7.91745 11.525C6.49762 11.525 5.34662 12.676 5.34662 14.0959C5.34661 15.5157 6.49762 16.6667 7.91745 16.6667C9.33728 16.6667 10.4883 15.5157 10.4883 14.0959C10.4883 12.676 9.33728 11.525 7.91745 11.525Z" fill="" stroke="" stroke-width="1.5"/>
|
||||
</svg>
|
||||
Filter
|
||||
</button>
|
||||
<button class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-3 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
See all
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="px-6 py-3.5 border-t border-gray-100 border-y bg-gray-50 dark:border-white/[0.05] dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">
|
||||
<div class="flex items-center gap-3">
|
||||
<div @click="handleSelectAll()"
|
||||
class="flex h-5 w-5 cursor-pointer items-center justify-center rounded-md border-[1.25px]"
|
||||
:class="selectAll ? 'border-blue-500 dark:border-blue-500 bg-blue-500' : 'bg-white dark:bg-white/0 border-gray-300 dark:border-gray-700'">
|
||||
<svg :class="selectAll ? 'block' : 'hidden'" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6668 3.5L5.25016 9.91667L2.3335 7" stroke="white" stroke-width="1.94437" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">Deal ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">Customer</th>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">Product/Service</th>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">Deal Value</th>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">Close Date</th>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">Status</th>
|
||||
<th class="px-6 py-3 font-medium text-gray-500 sm:px-6 text-theme-xs dark:text-gray-400 text-start">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="row in tableRowData" :key="row.id">
|
||||
<tr class="border-b border-gray-100 dark:border-white/[0.05]">
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<div class="flex items-center gap-3">
|
||||
<div @click="handleRowSelect(row.id)"
|
||||
class="flex h-5 w-5 cursor-pointer items-center justify-center rounded-md border-[1.25px]"
|
||||
:class="selectedRows.includes(row.id) ? 'border-blue-500 dark:border-blue-500 bg-blue-500' : 'bg-white dark:bg-white/0 border-gray-300 dark:border-gray-700'">
|
||||
<svg :class="selectedRows.includes(row.id) ? 'block' : 'hidden'" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6668 3.5L5.25016 9.91667L2.3335 7" stroke="white" stroke-width="1.94437" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<span class="block font-medium text-gray-700 text-theme-sm dark:text-gray-400" x-text="row.id"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-full font-medium text-sm"
|
||||
:class="[row.avatarBg, row.avatarColor]">
|
||||
<span x-text="row.initials"></span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="mb-0.5 block text-theme-sm font-medium text-gray-700 dark:text-gray-400" x-text="row.customerName"></span>
|
||||
<span class="text-gray-500 text-theme-sm dark:text-gray-400" x-text="row.customerEmail"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<p class="text-gray-700 text-theme-sm dark:text-gray-400" x-text="row.product"></p>
|
||||
</td>
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<p class="text-gray-700 text-theme-sm dark:text-gray-400" x-text="row.value"></p>
|
||||
</td>
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<p class="text-gray-700 text-theme-sm dark:text-gray-400" x-text="row.closeDate"></p>
|
||||
</td>
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<span class="text-theme-xs inline-block rounded-full px-2 py-0.5 font-medium"
|
||||
:class="getStatusClass(row.status)"
|
||||
x-text="row.status"></span>
|
||||
</td>
|
||||
<td class="px-4 sm:px-6 py-3.5">
|
||||
<button @click="deleteRow(row.id)">
|
||||
<svg class="text-gray-700 cursor-pointer size-5 hover:text-red-500 dark:text-gray-400 dark:hover:text-red-500"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
82
resources/views/components/ui/alert.blade.php
Normal file
82
resources/views/components/ui/alert.blade.php
Normal file
@@ -0,0 +1,82 @@
|
||||
{{-- resources/views/components/alert.blade.php --}}
|
||||
|
||||
@props([
|
||||
'variant' => 'info',
|
||||
'title' => '',
|
||||
'message' => '',
|
||||
'showLink' => false,
|
||||
'linkHref' => '#',
|
||||
'linkText' => 'Learn more'
|
||||
])
|
||||
|
||||
@php
|
||||
$variantClasses = [
|
||||
'success' => [
|
||||
'container' => 'border-green-500 bg-green-50 dark:border-green-500/30 dark:bg-green-500/15',
|
||||
'icon' => 'text-green-500',
|
||||
],
|
||||
'error' => [
|
||||
'container' => 'border-red-500 bg-red-50 dark:border-red-500/30 dark:bg-red-500/15',
|
||||
'icon' => 'text-red-500',
|
||||
],
|
||||
'warning' => [
|
||||
'container' => 'border-yellow-500 bg-yellow-50 dark:border-yellow-500/30 dark:bg-yellow-500/15',
|
||||
'icon' => 'text-yellow-500',
|
||||
],
|
||||
'info' => [
|
||||
'container' => 'border-blue-500 bg-blue-50 dark:border-blue-500/30 dark:bg-blue-500/15',
|
||||
'icon' => 'text-blue-500',
|
||||
],
|
||||
];
|
||||
|
||||
$icons = [
|
||||
'success' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.70186 12.0001C3.70186 7.41711 7.41711 3.70186 12.0001 3.70186C16.5831 3.70186 20.2984 7.41711 20.2984 12.0001C20.2984 16.5831 16.5831 20.2984 12.0001 20.2984C7.41711 20.2984 3.70186 16.5831 3.70186 12.0001ZM12.0001 1.90186C6.423 1.90186 1.90186 6.423 1.90186 12.0001C1.90186 17.5772 6.423 22.0984 12.0001 22.0984C17.5772 22.0984 22.0984 17.5772 22.0984 12.0001C22.0984 6.423 17.5772 1.90186 12.0001 1.90186ZM15.6197 10.7395C15.9712 10.388 15.9712 9.81819 15.6197 9.46672C15.2683 9.11525 14.6984 9.11525 14.347 9.46672L11.1894 12.6243L9.6533 11.0883C9.30183 10.7368 8.73198 10.7368 8.38051 11.0883C8.02904 11.4397 8.02904 12.0096 8.38051 12.3611L10.553 14.5335C10.7217 14.7023 10.9507 14.7971 11.1894 14.7971C11.428 14.7971 11.657 14.7023 11.8257 14.5335L15.6197 10.7395Z" fill=""></path>
|
||||
</svg>',
|
||||
'error' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.6501 12.0001C3.6501 7.38852 7.38852 3.6501 12.0001 3.6501C16.6117 3.6501 20.3501 7.38852 20.3501 12.0001C20.3501 16.6117 16.6117 20.3501 12.0001 20.3501C7.38852 20.3501 3.6501 16.6117 3.6501 12.0001ZM12.0001 1.8501C6.39441 1.8501 1.8501 6.39441 1.8501 12.0001C1.8501 17.6058 6.39441 22.1501 12.0001 22.1501C17.6058 22.1501 22.1501 17.6058 22.1501 12.0001C22.1501 6.39441 17.6058 1.8501 12.0001 1.8501ZM10.9992 7.52517C10.9992 8.07746 11.4469 8.52517 11.9992 8.52517H12.0002C12.5525 8.52517 13.0002 8.07746 13.0002 7.52517C13.0002 6.97289 12.5525 6.52517 12.0002 6.52517H11.9992C11.4469 6.52517 10.9992 6.97289 10.9992 7.52517ZM12.0002 17.3715C11.586 17.3715 11.2502 17.0357 11.2502 16.6215V10.945C11.2502 10.5308 11.586 10.195 12.0002 10.195C12.4144 10.195 12.7502 10.5308 12.7502 10.945V16.6215C12.7502 17.0357 12.4144 17.3715 12.0002 17.3715Z" fill=""></path>
|
||||
</svg>',
|
||||
'warning' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.3499 12.0004C20.3499 16.612 16.6115 20.3504 11.9999 20.3504C7.38832 20.3504 3.6499 16.612 3.6499 12.0004C3.6499 7.38881 7.38833 3.65039 11.9999 3.65039C16.6115 3.65039 20.3499 7.38881 20.3499 12.0004ZM11.9999 22.1504C17.6056 22.1504 22.1499 17.6061 22.1499 12.0004C22.1499 6.3947 17.6056 1.85039 11.9999 1.85039C6.39421 1.85039 1.8499 6.3947 1.8499 12.0004C1.8499 17.6061 6.39421 22.1504 11.9999 22.1504ZM13.0008 16.4753C13.0008 15.923 12.5531 15.4753 12.0008 15.4753L11.9998 15.4753C11.4475 15.4753 10.9998 15.923 10.9998 16.4753C10.9998 17.0276 11.4475 17.4753 11.9998 17.4753L12.0008 17.4753C12.5531 17.4753 13.0008 17.0276 13.0008 16.4753ZM11.9998 6.62898C12.414 6.62898 12.7498 6.96476 12.7498 7.37898L12.7498 13.0555C12.7498 13.4697 12.414 13.8055 11.9998 13.8055C11.5856 13.8055 11.2498 13.4697 11.2498 13.0555L11.2498 7.37898C11.2498 6.96476 11.5856 6.62898 11.9998 6.62898Z" fill="#F04438"></path>
|
||||
</svg>',
|
||||
'info' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.6501 11.9996C3.6501 7.38803 7.38852 3.64961 12.0001 3.64961C16.6117 3.64961 20.3501 7.38803 20.3501 11.9996C20.3501 16.6112 16.6117 20.3496 12.0001 20.3496C7.38852 20.3496 3.6501 16.6112 3.6501 11.9996ZM12.0001 1.84961C6.39441 1.84961 1.8501 6.39392 1.8501 11.9996C1.8501 17.6053 6.39441 22.1496 12.0001 22.1496C17.6058 22.1496 22.1501 17.6053 22.1501 11.9996C22.1501 6.39392 17.6058 1.84961 12.0001 1.84961ZM10.9992 7.52468C10.9992 8.07697 11.4469 8.52468 11.9992 8.52468H12.0002C12.5525 8.52468 13.0002 8.07697 13.0002 7.52468C13.0002 6.9724 12.5525 6.52468 12.0002 6.52468H11.9992C11.4469 6.52468 10.9992 6.9724 10.9992 7.52468ZM12.0002 17.371C11.586 17.371 11.2502 17.0352 11.2502 16.621V10.9445C11.2502 10.5303 11.586 10.1945 12.0002 10.1945C12.4144 10.1945 12.7502 10.5303 12.7502 10.9445V16.621C12.7502 17.0352 12.4144 17.371 12.0002 17.371Z" fill=""></path>
|
||||
</svg>',
|
||||
];
|
||||
|
||||
$containerClass = $variantClasses[$variant]['container'] ?? $variantClasses['info']['container'];
|
||||
$iconClass = $variantClasses[$variant]['icon'] ?? $variantClasses['info']['icon'];
|
||||
$icon = $icons[$variant] ?? $icons['info'];
|
||||
@endphp
|
||||
|
||||
<div class="rounded-xl border p-4 {{ $containerClass }}">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="-mt-0.5 {{ $iconClass }}">
|
||||
{!! $icon !!}
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
@if($title)
|
||||
<h4 class="mb-1 text-sm font-semibold text-gray-800 dark:text-white/90">
|
||||
{{ $title }}
|
||||
</h4>
|
||||
@endif
|
||||
|
||||
@if($message)
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">{{ $message }}</p>
|
||||
@endif
|
||||
|
||||
@if($showLink)
|
||||
<a
|
||||
href="{{ $linkHref }}"
|
||||
class="inline-block mt-3 text-sm font-medium text-gray-500 underline dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
>
|
||||
{{ $linkText }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
{{-- Slot for custom content --}}
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
49
resources/views/components/ui/avatar.blade.php
Normal file
49
resources/views/components/ui/avatar.blade.php
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
@props([
|
||||
'src' => '',
|
||||
'alt' => 'User Avatar',
|
||||
'size' => 'medium',
|
||||
'status' => 'none',
|
||||
])
|
||||
|
||||
@php
|
||||
$sizeClasses = [
|
||||
'xsmall' => 'h-6 w-6 max-w-6',
|
||||
'small' => 'h-8 w-8 max-w-8',
|
||||
'medium' => 'h-10 w-10 max-w-10',
|
||||
'large' => 'h-12 w-12 max-w-12',
|
||||
'xlarge' => 'h-14 w-14 max-w-14',
|
||||
'xxlarge' => 'h-16 w-16 max-w-16',
|
||||
];
|
||||
|
||||
$statusSizeClasses = [
|
||||
'xsmall' => 'h-1.5 w-1.5 max-w-1.5',
|
||||
'small' => 'h-2 w-2 max-w-2',
|
||||
'medium' => 'h-2.5 w-2.5 max-w-2.5',
|
||||
'large' => 'h-3 w-3 max-w-3',
|
||||
'xlarge' => 'h-3.5 w-3.5 max-w-3.5',
|
||||
'xxlarge' => 'h-4 w-4 max-w-4',
|
||||
];
|
||||
|
||||
$statusColorClasses = [
|
||||
'online' => 'bg-green-500',
|
||||
'offline' => 'bg-red-400',
|
||||
'busy' => 'bg-yellow-500',
|
||||
];
|
||||
|
||||
$sizeClass = $sizeClasses[$size] ?? $sizeClasses['medium'];
|
||||
$statusSizeClass = $statusSizeClasses[$size] ?? $statusSizeClasses['medium'];
|
||||
$statusColorClass = $statusColorClasses[$status] ?? '';
|
||||
@endphp
|
||||
|
||||
<div class="relative rounded-full {{ $sizeClass }}">
|
||||
<img
|
||||
src="{{ $src }}"
|
||||
alt="{{ $alt }}"
|
||||
class="h-full w-full object-cover rounded-full"
|
||||
/>
|
||||
|
||||
@if($status !== 'none')
|
||||
<span class="absolute bottom-0 right-0 rounded-full border-[1.5px] border-white dark:border-gray-900 {{ $statusSizeClass }} {{ $statusColorClass }}"></span>
|
||||
@endif
|
||||
</div>
|
||||
53
resources/views/components/ui/badge.blade.php
Normal file
53
resources/views/components/ui/badge.blade.php
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
@props([
|
||||
'variant' => 'light',
|
||||
'size' => 'md',
|
||||
'color' => 'primary',
|
||||
'startIcon' => null,
|
||||
'endIcon' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$baseStyles = 'inline-flex items-center px-2.5 py-0.5 justify-center gap-1 rounded-full font-medium capitalize';
|
||||
|
||||
$sizeStyles = [
|
||||
'sm' => 'text-xs',
|
||||
'md' => 'text-sm',
|
||||
];
|
||||
|
||||
$variants = [
|
||||
'light' => [
|
||||
'primary' => 'bg-blue-50 text-blue-500 dark:bg-blue-500/15 dark:text-blue-400',
|
||||
'success' => 'bg-green-50 text-green-600 dark:bg-green-500/15 dark:text-green-500',
|
||||
'error' => 'bg-red-50 text-red-600 dark:bg-red-500/15 dark:text-red-500',
|
||||
'warning' => 'bg-yellow-50 text-yellow-600 dark:bg-yellow-500/15 dark:text-orange-400',
|
||||
'info' => 'bg-sky-50 text-sky-500 dark:bg-sky-500/15 dark:text-sky-500',
|
||||
'light' => 'bg-gray-100 text-gray-700 dark:bg-white/5 dark:text-white/80',
|
||||
'dark' => 'bg-gray-500 text-white dark:bg-white/5 dark:text-white',
|
||||
],
|
||||
'solid' => [
|
||||
'primary' => 'bg-blue-500 text-white dark:text-white',
|
||||
'success' => 'bg-green-500 text-white dark:text-white',
|
||||
'error' => 'bg-red-500 text-white dark:text-white',
|
||||
'warning' => 'bg-yellow-500 text-white dark:text-white',
|
||||
'info' => 'bg-sky-500 text-white dark:text-white',
|
||||
'light' => 'bg-gray-400 dark:bg-white/5 text-white dark:text-white/80',
|
||||
'dark' => 'bg-gray-700 text-white dark:text-white',
|
||||
],
|
||||
];
|
||||
|
||||
$sizeClass = $sizeStyles[$size] ?? $sizeStyles['md'];
|
||||
$colorStyles = $variants[$variant][$color] ?? $variants['light']['primary'];
|
||||
@endphp
|
||||
|
||||
<span class="{{ $baseStyles }} {{ $sizeClass }} {{ $colorStyles }}" {{ $attributes }}>
|
||||
@if($startIcon)
|
||||
{!! $startIcon !!}
|
||||
@endif
|
||||
|
||||
{{ $slot }}
|
||||
|
||||
@if($endIcon)
|
||||
{!! $endIcon !!}
|
||||
@endif
|
||||
</span>
|
||||
61
resources/views/components/ui/button.blade.php
Normal file
61
resources/views/components/ui/button.blade.php
Normal file
@@ -0,0 +1,61 @@
|
||||
@props([
|
||||
'size' => 'md',
|
||||
'variant' => 'primary',
|
||||
'startIcon' => null,
|
||||
'endIcon' => null,
|
||||
'className' => '',
|
||||
'disabled' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
// Base classes
|
||||
$base = 'inline-flex items-center justify-center font-medium gap-2 rounded-lg transition';
|
||||
|
||||
// Size map
|
||||
$sizeMap = [
|
||||
'sm' => 'px-4 py-3 text-sm',
|
||||
'md' => 'px-5 py-3.5 text-sm',
|
||||
];
|
||||
$sizeClass = $sizeMap[$size] ?? $sizeMap['md'];
|
||||
|
||||
// Variant map
|
||||
$variantMap = [
|
||||
'primary' => 'bg-brand-500 text-white shadow-theme-xs hover:bg-brand-600 disabled:bg-brand-300',
|
||||
'outline' => 'bg-white text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:ring-gray-700 dark:hover:bg-white/[0.03] dark:hover:text-gray-300',
|
||||
];
|
||||
$variantClass = $variantMap[$variant] ?? $variantMap['primary'];
|
||||
|
||||
// disabled classes
|
||||
$disabledClass = $disabled ? 'cursor-not-allowed opacity-50' : '';
|
||||
|
||||
// final classes (merge user className too)
|
||||
$classes = trim("{$base} {$sizeClass} {$variantClass} {$className} {$disabledClass}");
|
||||
@endphp
|
||||
|
||||
<button
|
||||
{{ $attributes->merge(['class' => $classes, 'type' => $attributes->get('type', 'button')]) }}
|
||||
@if($disabled) disabled @endif
|
||||
>
|
||||
{{-- start icon: priority — named slot 'startIcon' first, then startIcon prop if it's a HtmlString --}}
|
||||
@if (isset($__env) && $slot->isEmpty() === false) @endif
|
||||
|
||||
@hasSection('startIcon')
|
||||
<span class="flex items-center">
|
||||
@yield('startIcon')
|
||||
</span>
|
||||
@elseif($startIcon)
|
||||
<span class="flex items-center">{!! $startIcon !!}</span>
|
||||
@endif
|
||||
|
||||
{{-- main slot --}}
|
||||
{{ $slot }}
|
||||
|
||||
{{-- end icon: named slot 'endIcon' first, then endIcon prop --}}
|
||||
@hasSection('endIcon')
|
||||
<span class="flex items-center">
|
||||
@yield('endIcon')
|
||||
</span>
|
||||
@elseif($endIcon)
|
||||
<span class="flex items-center">{!! $endIcon !!}</span>
|
||||
@endif
|
||||
</button>
|
||||
92
resources/views/components/ui/flash-message.blade.php
Normal file
92
resources/views/components/ui/flash-message.blade.php
Normal file
@@ -0,0 +1,92 @@
|
||||
@if (session()->has('success') || session()->has('error') || session()->has('warning') || session()->has('info') || session()->has('status'))
|
||||
@php
|
||||
$variant = 'info';
|
||||
$message = '';
|
||||
$title = '';
|
||||
|
||||
if (session()->has('success')) {
|
||||
$variant = 'success';
|
||||
$message = session('success');
|
||||
$title = 'Success';
|
||||
} elseif (session()->has('error')) {
|
||||
$variant = 'error';
|
||||
$message = session('error');
|
||||
$title = 'Error';
|
||||
} elseif (session()->has('warning')) {
|
||||
$variant = 'warning';
|
||||
$message = session('warning');
|
||||
$title = 'Warning';
|
||||
} elseif (session()->has('info')) {
|
||||
$variant = 'info';
|
||||
$message = session('info');
|
||||
$title = 'Information';
|
||||
} elseif (session()->has('status')) {
|
||||
$variant = 'success'; // Default status to success/info style
|
||||
$message = session('status');
|
||||
$title = 'Notification';
|
||||
}
|
||||
|
||||
// Copy styles from ui.alert
|
||||
$variantClasses = [
|
||||
'success' => [
|
||||
'container' => 'border-green-500 bg-green-50 dark:border-green-500/30 dark:bg-green-500/15',
|
||||
'icon' => 'text-green-500',
|
||||
],
|
||||
'error' => [
|
||||
'container' => 'border-red-500 bg-red-50 dark:border-red-500/30 dark:bg-red-500/15',
|
||||
'icon' => 'text-red-500',
|
||||
],
|
||||
'warning' => [
|
||||
'container' => 'border-yellow-500 bg-yellow-50 dark:border-yellow-500/30 dark:bg-yellow-500/15',
|
||||
'icon' => 'text-yellow-500',
|
||||
],
|
||||
'info' => [
|
||||
'container' => 'border-blue-500 bg-blue-50 dark:border-blue-500/30 dark:bg-blue-500/15',
|
||||
'icon' => 'text-blue-500',
|
||||
],
|
||||
];
|
||||
|
||||
$icons = [
|
||||
'success' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.70186 12.0001C3.70186 7.41711 7.41711 3.70186 12.0001 3.70186C16.5831 3.70186 20.2984 7.41711 20.2984 12.0001C20.2984 16.5831 16.5831 20.2984 12.0001 20.2984C7.41711 20.2984 3.70186 16.5831 3.70186 12.0001ZM12.0001 1.90186C6.423 1.90186 1.90186 6.423 1.90186 12.0001C1.90186 17.5772 6.423 22.0984 12.0001 22.0984C17.5772 22.0984 22.0984 17.5772 22.0984 12.0001C22.0984 6.423 17.5772 1.90186 12.0001 1.90186ZM15.6197 10.7395C15.9712 10.388 15.9712 9.81819 15.6197 9.46672C15.2683 9.11525 14.6984 9.11525 14.347 9.46672L11.1894 12.6243L9.6533 11.0883C9.30183 10.7368 8.73198 10.7368 8.38051 11.0883C8.02904 11.4397 8.02904 12.0096 8.38051 12.3611L10.553 14.5335C10.7217 14.7023 10.9507 14.7971 11.1894 14.7971C11.428 14.7971 11.657 14.7023 11.8257 14.5335L15.6197 10.7395Z" fill=""></path></svg>',
|
||||
'error' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.6501 12.0001C3.6501 7.38852 7.38852 3.6501 12.0001 3.6501C16.6117 3.6501 20.3501 7.38852 20.3501 12.0001C20.3501 16.6117 16.6117 20.3501 12.0001 20.3501C7.38852 20.3501 3.6501 16.6117 3.6501 12.0001ZM12.0001 1.8501C6.39441 1.8501 1.8501 6.39441 1.8501 12.0001C1.8501 17.6058 6.39441 22.1501 12.0001 22.1501C17.6058 22.1501 22.1501 17.6058 22.1501 12.0001C22.1501 6.39441 17.6058 1.8501 12.0001 1.8501ZM10.9992 7.52517C10.9992 8.07746 11.4469 8.52517 11.9992 8.52517H12.0002C12.5525 8.52517 13.0002 8.07746 13.0002 7.52517C13.0002 6.97289 12.5525 6.52517 12.0002 6.52517H11.9992C11.4469 6.52517 10.9992 6.97289 10.9992 7.52517ZM12.0002 17.3715C11.586 17.3715 11.2502 17.0357 11.2502 16.6215V10.945C11.2502 10.5308 11.586 10.195 12.0002 10.195C12.4144 10.195 12.7502 10.5308 12.7502 10.945V16.6215C12.7502 17.0357 12.4144 17.3715 12.0002 17.3715Z" fill=""></path></svg>',
|
||||
'warning' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M20.3499 12.0004C20.3499 16.612 16.6115 20.3504 11.9999 20.3504C7.38832 20.3504 3.6499 16.612 3.6499 12.0004C3.6499 7.38881 7.38833 3.65039 11.9999 3.65039C16.6115 3.65039 20.3499 7.38881 20.3499 12.0004ZM11.9999 22.1504C17.6056 22.1504 22.1499 17.6061 22.1499 12.0004C22.1499 6.3947 17.6056 1.85039 11.9999 1.85039C6.39421 1.85039 1.8499 6.3947 1.8499 12.0004C1.8499 17.6061 6.39421 22.1504 11.9999 22.1504ZM13.0008 16.4753C13.0008 15.923 12.5531 15.4753 12.0008 15.4753L11.9998 15.4753C11.4475 15.4753 10.9998 15.923 10.9998 16.4753C10.9998 17.0276 11.4475 17.4753 11.9998 17.4753L12.0008 17.4753C12.5531 17.4753 13.0008 17.0276 13.0008 16.4753ZM11.9998 6.62898C12.414 6.62898 12.7498 6.96476 12.7498 7.37898L12.7498 13.0555C12.7498 13.4697 12.414 13.8055 11.9998 13.8055C11.5856 13.8055 11.2498 13.4697 11.2498 13.0555L11.2498 7.37898C11.2498 6.96476 11.5856 6.62898 11.9998 6.62898Z" fill="#F04438"></path></svg>',
|
||||
'info' => '<svg class="fill-current" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.6501 11.9996C3.6501 7.38803 7.38852 3.64961 12.0001 3.64961C16.6117 3.64961 20.3501 7.38803 20.3501 11.9996C20.3501 16.6112 16.6117 20.3496 12.0001 20.3496C7.38852 20.3496 3.6501 16.6112 3.6501 11.9996ZM12.0001 1.84961C6.39441 1.84961 1.8501 6.39392 1.8501 11.9996C1.8501 17.6053 6.39441 22.1496 12.0001 22.1496C17.6058 22.1496 22.1501 17.6053 22.1501 11.9996C22.1501 6.39392 17.6058 1.84961 12.0001 1.84961ZM10.9992 7.52468C10.9992 8.07697 11.4469 8.52468 11.9992 8.52468H12.0002C12.5525 8.52468 13.0002 8.07697 13.0002 7.52468C13.0002 6.9724 12.5525 6.52468 12.0002 6.52468H11.9992C11.4469 6.52468 10.9992 6.9724 10.9992 7.52468ZM12.0002 17.371C11.586 17.371 11.2502 17.0352 11.2502 16.621V10.9445C11.2502 10.5303 11.586 10.1945 12.0002 10.1945C12.4144 10.1945 12.7502 10.5303 12.7502 10.9445V16.621C12.7502 17.0352 12.4144 17.371 12.0002 17.371Z" fill=""></path></svg>',
|
||||
];
|
||||
|
||||
$containerClass = $variantClasses[$variant]['container'] ?? $variantClasses['info']['container'];
|
||||
$iconClass = $variantClasses[$variant]['icon'] ?? $variantClasses['info']['icon'];
|
||||
$icon = $icons[$variant] ?? $icons['info'];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-data="{ show: true }"
|
||||
x-show="show"
|
||||
x-init="setTimeout(() => show = false, 8000)"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-2"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 translate-y-2"
|
||||
class="fixed top-32 right-5 left-5 sm:left-auto z-[999999] sm:w-full sm:max-w-sm"
|
||||
style="display: none;" {{-- Prevent Flash of Unstyled Content --}}
|
||||
>
|
||||
<div class="rounded-xl border p-4 shadow-lg {{ $containerClass }} relative">
|
||||
<button @click="show = false" class="absolute top-2 right-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 focus:outline-none">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
||||
</button>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="-mt-0.5 {{ $iconClass }}">
|
||||
{!! $icon !!}
|
||||
</div>
|
||||
|
||||
<div class="flex-1 pr-4"> {{-- Add padding right for close button --}}
|
||||
<h4 class="mb-1 text-sm font-semibold text-gray-800 dark:text-white/90">
|
||||
{{ $title }}
|
||||
</h4>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">{{ $message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
78
resources/views/components/ui/modal.blade.php
Normal file
78
resources/views/components/ui/modal.blade.php
Normal file
@@ -0,0 +1,78 @@
|
||||
@props([
|
||||
'isOpen' => false,
|
||||
'showCloseButton' => true,
|
||||
'containerClass' => 'max-w-[500px]',
|
||||
])
|
||||
|
||||
<div {{ $attributes->merge([
|
||||
'x-data' => '{
|
||||
open: ' . ($isOpen ? 'true' : 'false') . ',
|
||||
init() {
|
||||
this.$watch(\'open\', value => {
|
||||
if (value) {
|
||||
document.body.style.overflow = \'hidden\';
|
||||
} else {
|
||||
document.body.style.overflow = \'unset\';
|
||||
}
|
||||
});
|
||||
}
|
||||
}'
|
||||
]) }}
|
||||
x-modelable="open"
|
||||
x-show="open"
|
||||
x-cloak
|
||||
@keydown.escape.window="open = false"
|
||||
class="relative z-99999" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||
|
||||
<!-- Backdrop -->
|
||||
<div x-show="open"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="fixed inset-0 bg-gray-500/75 dark:bg-gray-900/80 backdrop-blur-sm transition-opacity"
|
||||
aria-hidden="true">
|
||||
</div>
|
||||
|
||||
<!-- Scrollable Container -->
|
||||
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
|
||||
<!-- Modal Panel -->
|
||||
<div @click.stop x-show="open"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
class="relative transform overflow-hidden rounded-2xl bg-white text-left shadow-xl transition-all dark:bg-gray-900 sm:my-8 w-full {{ $containerClass }}">
|
||||
|
||||
<!-- Close Button -->
|
||||
@if ($showCloseButton)
|
||||
<div class="absolute right-4 top-4 z-10 hidden sm:block">
|
||||
<button @click="open = false" type="button" class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:bg-gray-900 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<span class="sr-only">Close</span>
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Modal Content -->
|
||||
<div>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
[x-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
29
resources/views/components/ui/youtube-embed.blade.php
Normal file
29
resources/views/components/ui/youtube-embed.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
@props([
|
||||
'videoId' => '',
|
||||
'aspectRatio' => '16:9',
|
||||
'title' => 'YouTube video',
|
||||
'className' => ''
|
||||
])
|
||||
|
||||
@php
|
||||
$aspectRatioClasses = [
|
||||
'16:9' => 'aspect-video',
|
||||
'4:3' => 'aspect-4/3',
|
||||
'21:9' => 'aspect-21/9',
|
||||
'1:1' => 'aspect-square',
|
||||
];
|
||||
|
||||
$aspectRatioClass = $aspectRatioClasses[$aspectRatio] ?? $aspectRatioClasses['16:9'];
|
||||
@endphp
|
||||
|
||||
<div class="overflow-hidden rounded-lg {{ $aspectRatioClass }} {{ $className }}">
|
||||
<iframe
|
||||
src="https://www.youtube.com/embed/{{ $videoId }}"
|
||||
title="{{ $title }}"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
class="w-full h-full"
|
||||
></iframe>
|
||||
</div>
|
||||
33
resources/views/errors/403.blade.php
Normal file
33
resources/views/errors/403.blade.php
Normal file
@@ -0,0 +1,33 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$currentYear = date('Y');
|
||||
@endphp
|
||||
<div class="relative flex flex-col items-center justify-center min-h-screen p-6 overflow-hidden z-1">
|
||||
{{-- common grid shape --}}
|
||||
<x-common.common-grid-shape />
|
||||
<!-- Centered Content -->
|
||||
<div class="mx-auto w-full max-w-[242px] text-center sm:max-w-[472px]">
|
||||
<h1 class="mb-8 font-bold text-gray-800 text-title-md dark:text-white/90 xl:text-title-2xl">
|
||||
FORBIDDEN
|
||||
</h1>
|
||||
|
||||
<img src="/images/error/403.svg" alt="403" class="dark:hidden" />
|
||||
<img src="/images/error/403-dark.svg" alt="403" class="hidden dark:block" />
|
||||
|
||||
<p class="mt-10 mb-6 text-base text-gray-700 dark:text-gray-400 sm:text-lg">
|
||||
You don't have permission to access this page.
|
||||
</p>
|
||||
|
||||
<a href="/"
|
||||
class="inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-5 py-3.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
Back to Home Page
|
||||
</a>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<p class="absolute text-sm text-center text-gray-500 -translate-x-1/2 bottom-6 left-1/2 dark:text-gray-400">
|
||||
© {{ $currentYear }} - {{ config('app.name') }}
|
||||
</p>
|
||||
</div>
|
||||
@endsection
|
||||
33
resources/views/errors/404.blade.php
Normal file
33
resources/views/errors/404.blade.php
Normal file
@@ -0,0 +1,33 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$currentYear = date('Y');
|
||||
@endphp
|
||||
<div class="relative flex flex-col items-center justify-center min-h-screen p-6 overflow-hidden z-1">
|
||||
{{-- common grid shape --}}
|
||||
<x-common.common-grid-shape />
|
||||
<!-- Centered Content -->
|
||||
<div class="mx-auto w-full max-w-[242px] text-center sm:max-w-[472px]">
|
||||
<h1 class="mb-8 font-bold text-gray-800 text-title-md dark:text-white/90 xl:text-title-2xl">
|
||||
ERROR
|
||||
</h1>
|
||||
|
||||
<img src="/images/error/404.svg" alt="404" class="dark:hidden" />
|
||||
<img src="/images/error/404-dark.svg" alt="404" class="hidden dark:block" />
|
||||
|
||||
<p class="mt-10 mb-6 text-base text-gray-700 dark:text-gray-400 sm:text-lg">
|
||||
We can't seem to find the page you are looking for!
|
||||
</p>
|
||||
|
||||
<a href="/"
|
||||
class="inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-5 py-3.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
Back to Home Page
|
||||
</a>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<p class="absolute text-sm text-center text-gray-500 -translate-x-1/2 bottom-6 left-1/2 dark:text-gray-400">
|
||||
© {{ $currentYear }} - {{ config('app.name') }}
|
||||
</p>
|
||||
</div>
|
||||
@endsection
|
||||
30
resources/views/errors/500.blade.php
Normal file
30
resources/views/errors/500.blade.php
Normal file
@@ -0,0 +1,30 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$currentYear = date('Y');
|
||||
@endphp
|
||||
<div class="relative flex flex-col items-center justify-center min-h-screen p-6 overflow-hidden z-1">
|
||||
<x-common.common-grid-shape />
|
||||
<div class="mx-auto w-full max-w-[242px] text-center sm:max-w-[472px]">
|
||||
<h1 class="mb-8 font-bold text-gray-800 text-title-md dark:text-white/90 xl:text-title-2xl">
|
||||
ERROR
|
||||
</h1>
|
||||
|
||||
<img src="/images/error/500.svg" alt="500" class="dark:hidden" />
|
||||
<img src="/images/error/500-dark.svg" alt="500" class="hidden dark:block" />
|
||||
|
||||
<p class="mt-10 mb-6 text-base text-gray-700 dark:text-gray-400 sm:text-lg">
|
||||
Something went wrong on our end. Please try again later.
|
||||
</p>
|
||||
|
||||
<a href="/"
|
||||
class="inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-5 py-3.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
Back to Home Page
|
||||
</a>
|
||||
</div>
|
||||
<p class="absolute text-sm text-center text-gray-500 -translate-x-1/2 bottom-6 left-1/2 dark:text-gray-400">
|
||||
© {{ $currentYear }} - {{ config('app.name') }}
|
||||
</p>
|
||||
</div>
|
||||
@endsection
|
||||
30
resources/views/errors/503.blade.php
Normal file
30
resources/views/errors/503.blade.php
Normal file
@@ -0,0 +1,30 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$currentYear = date('Y');
|
||||
@endphp
|
||||
<div class="relative flex flex-col items-center justify-center min-h-screen p-6 overflow-hidden z-1">
|
||||
<x-common.common-grid-shape />
|
||||
<div class="mx-auto w-full max-w-[242px] text-center sm:max-w-[472px]">
|
||||
<h1 class="mb-8 font-bold text-gray-800 text-title-md dark:text-white/90 xl:text-title-2xl">
|
||||
MAINTENANCE
|
||||
</h1>
|
||||
|
||||
<img src="/images/error/503.svg" alt="503" class="dark:hidden" />
|
||||
<img src="/images/error/503-dark.svg" alt="503" class="hidden dark:block" />
|
||||
|
||||
<p class="mt-10 mb-6 text-base text-gray-700 dark:text-gray-400 sm:text-lg">
|
||||
The service is currently unavailable. We are performing maintenance.
|
||||
</p>
|
||||
|
||||
<a href="/"
|
||||
class="inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-5 py-3.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
Refresh Page
|
||||
</a>
|
||||
</div>
|
||||
<p class="absolute text-sm text-center text-gray-500 -translate-x-1/2 bottom-6 left-1/2 dark:text-gray-400">
|
||||
© {{ $currentYear }} - {{ config('app.name') }}
|
||||
</p>
|
||||
</div>
|
||||
@endsection
|
||||
123
resources/views/layouts/app-header.blade.php
Normal file
123
resources/views/layouts/app-header.blade.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<header
|
||||
class="sticky top-0 flex w-full bg-white border-gray-200 z-99999 dark:border-gray-800 dark:bg-gray-900 xl:border-b"
|
||||
x-data="{
|
||||
isApplicationMenuOpen: false,
|
||||
toggleApplicationMenu() {
|
||||
this.isApplicationMenuOpen = !this.isApplicationMenuOpen;
|
||||
}
|
||||
}">
|
||||
<div class="flex flex-col items-center justify-between grow xl:flex-row xl:px-6">
|
||||
<div
|
||||
class="flex items-center justify-between w-full gap-2 px-3 py-3 border-b border-gray-200 dark:border-gray-800 sm:gap-4 xl:justify-normal xl:border-b-0 xl:px-0 lg:py-4">
|
||||
|
||||
<!-- Desktop Sidebar Toggle Button (visible on xl and up) -->
|
||||
<button
|
||||
class="hidden xl:flex items-center justify-center w-10 h-10 text-gray-500 border border-gray-200 rounded-lg dark:border-gray-800 dark:text-gray-400 lg:h-11 lg:w-11"
|
||||
:class="{ 'bg-gray-100 dark:bg-white/[0.03]': !$store.sidebar.isExpanded }"
|
||||
@click="$store.sidebar.toggleExpanded()" aria-label="Toggle Sidebar">
|
||||
<svg x-show="!$store.sidebar.isMobileOpen" width="16" height="12" viewBox="0 0 16 12" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M0.583252 1C0.583252 0.585788 0.919038 0.25 1.33325 0.25H14.6666C15.0808 0.25 15.4166 0.585786 15.4166 1C15.4166 1.41421 15.0808 1.75 14.6666 1.75L1.33325 1.75C0.919038 1.75 0.583252 1.41422 0.583252 1ZM0.583252 11C0.583252 10.5858 0.919038 10.25 1.33325 10.25L14.6666 10.25C15.0808 10.25 15.4166 10.5858 15.4166 11C15.4166 11.4142 15.0808 11.75 14.6666 11.75L1.33325 11.75C0.919038 11.75 0.583252 11.4142 0.583252 11ZM1.33325 5.25C0.919038 5.25 0.583252 5.58579 0.583252 6C0.583252 6.41421 0.919038 6.75 1.33325 6.75L7.99992 6.75C8.41413 6.75 8.74992 6.41421 8.74992 6C8.74992 5.58579 8.41413 5.25 7.99992 5.25L1.33325 5.25Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<svg x-show="$store.sidebar.isMobileOpen" class="fill-current" width="24" height="24" viewBox="0 0 24 24"
|
||||
fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Mobile Menu Toggle Button (visible below xl) -->
|
||||
<button
|
||||
class="flex xl:hidden items-center justify-center w-10 h-10 text-gray-500 rounded-lg dark:text-gray-400 lg:h-11 lg:w-11"
|
||||
:class="{ 'bg-gray-100 dark:bg-white/[0.03]': $store.sidebar.isMobileOpen }"
|
||||
@click="$store.sidebar.toggleMobileOpen()" aria-label="Toggle Mobile Menu">
|
||||
<svg x-show="!$store.sidebar.isMobileOpen" width="16" height="12" viewBox="0 0 16 12" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M0.583252 1C0.583252 0.585788 0.919038 0.25 1.33325 0.25H14.6666C15.0808 0.25 15.4166 0.585786 15.4166 1C15.4166 1.41421 15.0808 1.75 14.6666 1.75L1.33325 1.75C0.919038 1.75 0.583252 1.41422 0.583252 1ZM0.583252 11C0.583252 10.5858 0.919038 10.25 1.33325 10.25L14.6666 10.25C15.0808 10.25 15.4166 10.5858 15.4166 11C15.4166 11.4142 15.0808 11.75 14.6666 11.75L1.33325 11.75C0.919038 11.75 0.583252 11.4142 0.583252 11ZM1.33325 5.25C0.919038 5.25 0.583252 5.58579 0.583252 6C0.583252 6.41421 0.919038 6.75 1.33325 6.75L7.99992 6.75C8.41413 6.75 8.74992 6.41421 8.74992 6C8.74992 5.58579 8.41413 5.25 7.99992 5.25L1.33325 5.25Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<svg x-show="$store.sidebar.isMobileOpen" class="fill-current" width="24" height="24" viewBox="0 0 24 24"
|
||||
fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Logo (mobile only) -->
|
||||
<a href="{{ route('home') }}" class="xl:hidden">
|
||||
<img class="dark:hidden" src="/images/logo/logo.svg" alt="Logo" />
|
||||
<img class="hidden dark:block" src="/images/logo/logo-dark.svg" alt="Logo" />
|
||||
</a>
|
||||
|
||||
<!-- Application Menu Toggle (mobile only) -->
|
||||
<button @click="toggleApplicationMenu()"
|
||||
class="flex items-center justify-center w-10 h-10 text-gray-700 rounded-lg z-99999 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800 xl:hidden">
|
||||
<!-- Dots Icon -->
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M5.99902 10.4951C6.82745 10.4951 7.49902 11.1667 7.49902 11.9951V12.0051C7.49902 12.8335 6.82745 13.5051 5.99902 13.5051C5.1706 13.5051 4.49902 12.8335 4.49902 12.0051V11.9951C4.49902 11.1667 5.1706 10.4951 5.99902 10.4951ZM17.999 10.4951C18.8275 10.4951 19.499 11.1667 19.499 11.9951V12.0051C19.499 12.8335 18.8275 13.5051 17.999 13.5051C17.1706 13.5051 16.499 12.8335 16.499 12.0051V11.9951C16.499 11.1667 17.1706 10.4951 17.999 10.4951ZM13.499 11.9951C13.499 11.1667 12.8275 10.4951 11.999 10.4951C11.1706 10.4951 10.499 11.1667 10.499 11.9951V12.0051C10.499 12.8335 11.1706 13.5051 11.999 13.5051C12.8275 13.5051 13.499 12.8335 13.499 12.0051V11.9951Z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Search Bar (desktop only) -->
|
||||
<div class="hidden xl:block">
|
||||
<form>
|
||||
<div class="relative">
|
||||
<span class="absolute -translate-y-1/2 pointer-events-none left-4 top-1/2">
|
||||
<!-- Search Icon -->
|
||||
<svg class="fill-gray-500 dark:fill-gray-400" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M3.04175 9.37363C3.04175 5.87693 5.87711 3.04199 9.37508 3.04199C12.8731 3.04199 15.7084 5.87693 15.7084 9.37363C15.7084 12.8703 12.8731 15.7053 9.37508 15.7053C5.87711 15.7053 3.04175 12.8703 3.04175 9.37363ZM9.37508 1.54199C5.04902 1.54199 1.54175 5.04817 1.54175 9.37363C1.54175 13.6991 5.04902 17.2053 9.37508 17.2053C11.2674 17.2053 13.003 16.5344 14.357 15.4176L17.177 18.238C17.4699 18.5309 17.9448 18.5309 18.2377 18.238C18.5306 17.9451 18.5306 17.4703 18.2377 17.1774L15.418 14.3573C16.5365 13.0033 17.2084 11.2669 17.2084 9.37363C17.2084 5.04817 13.7011 1.54199 9.37508 1.54199Z"
|
||||
fill="" />
|
||||
</svg>
|
||||
</span>
|
||||
<input type="text" placeholder="Search or type command..."
|
||||
class="dark:bg-dark-900 h-11 w-full rounded-lg border border-gray-200 bg-transparent py-2.5 pl-12 pr-14 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-800 dark:bg-white/3 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800 xl:w-[430px]" />
|
||||
<button
|
||||
class="absolute right-2.5 top-1/2 inline-flex -translate-y-1/2 items-center gap-0.5 rounded-lg border border-gray-200 bg-gray-50 px-[7px] py-[4.5px] text-xs -tracking-[0.2px] text-gray-500 dark:border-gray-800 dark:bg-white/[0.03] dark:text-gray-400">
|
||||
<span> ⌘ </span>
|
||||
<span> K </span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Application Menu (mobile) and Right Side Actions (desktop) -->
|
||||
<div :class="isApplicationMenuOpen ? 'flex' : 'hidden'"
|
||||
class="items-center justify-between w-full gap-4 px-5 py-4 xl:flex shadow-theme-md xl:justify-end xl:px-0 xl:shadow-none">
|
||||
<div class="flex items-center gap-2 2xsm:gap-3">
|
||||
<!-- Theme Toggle Button -->
|
||||
<button
|
||||
class="relative flex items-center justify-center text-gray-500 transition-colors bg-white border border-gray-200 rounded-full hover:text-dark-900 h-11 w-11 hover:bg-gray-100 hover:text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
|
||||
@click="$store.theme.toggle()">
|
||||
<svg class="hidden dark:block" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
<svg class="dark:hidden" width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Notification Dropdown -->
|
||||
<x-header.notification-dropdown />
|
||||
</div>
|
||||
|
||||
<!-- User Dropdown -->
|
||||
<x-header.user-dropdown />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
144
resources/views/layouts/app.blade.php
Normal file
144
resources/views/layouts/app.blade.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="h-full">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ $title ?? 'Dashboard' }} | {{ config('app.name') }}</title>
|
||||
|
||||
<!-- Scripts -->
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
|
||||
<!-- Alpine.js -->
|
||||
{{-- <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script> --}}
|
||||
|
||||
<!-- Theme Store -->
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('theme', {
|
||||
init() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' :
|
||||
'light';
|
||||
this.theme = savedTheme || systemTheme;
|
||||
this.updateTheme();
|
||||
},
|
||||
theme: 'light',
|
||||
toggle() {
|
||||
this.theme = this.theme === 'light' ? 'dark' : 'light';
|
||||
localStorage.setItem('theme', this.theme);
|
||||
this.updateTheme();
|
||||
},
|
||||
updateTheme() {
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
if (this.theme === 'dark') {
|
||||
html.classList.add('dark');
|
||||
body.classList.add('dark', 'bg-gray-900');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
body.classList.remove('dark', 'bg-gray-900');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Alpine.store('sidebar', {
|
||||
// Initialize based on screen size
|
||||
isExpanded: window.innerWidth >= 1280, // true for desktop, false for mobile
|
||||
isMobileOpen: false,
|
||||
isHovered: false,
|
||||
|
||||
toggleExpanded() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
// When toggling desktop sidebar, ensure mobile menu is closed
|
||||
this.isMobileOpen = false;
|
||||
},
|
||||
|
||||
toggleMobileOpen() {
|
||||
this.isMobileOpen = !this.isMobileOpen;
|
||||
// Don't modify isExpanded when toggling mobile menu
|
||||
},
|
||||
|
||||
setMobileOpen(val) {
|
||||
this.isMobileOpen = val;
|
||||
},
|
||||
|
||||
setHovered(val) {
|
||||
// Only allow hover effects on desktop when sidebar is collapsed
|
||||
if (window.innerWidth >= 1280 && !this.isExpanded) {
|
||||
this.isHovered = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Apply dark mode immediately to prevent flash -->
|
||||
<script>
|
||||
(function() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
const theme = savedTheme || systemTheme;
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.body.classList.add('dark', 'bg-gray-900');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.body.classList.remove('dark', 'bg-gray-900');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Cropper.js -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body
|
||||
x-data="{ 'loaded': true}"
|
||||
x-init="$store.sidebar.isExpanded = window.innerWidth >= 1280;
|
||||
const checkMobile = () => {
|
||||
if (window.innerWidth < 1280) {
|
||||
$store.sidebar.setMobileOpen(false);
|
||||
$store.sidebar.isExpanded = false;
|
||||
} else {
|
||||
$store.sidebar.isMobileOpen = false;
|
||||
$store.sidebar.isExpanded = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener('resize', checkMobile);">
|
||||
|
||||
{{-- Flash Message Component --}}
|
||||
<x-ui.flash-message />
|
||||
|
||||
{{-- preloader --}}
|
||||
<x-common.preloader/>
|
||||
{{-- preloader end --}}
|
||||
|
||||
<div class="min-h-screen xl:flex">
|
||||
@include('layouts.backdrop')
|
||||
@include('layouts.sidebar')
|
||||
|
||||
<div class="flex-1 transition-all duration-300 ease-in-out"
|
||||
:class="{
|
||||
'xl:ml-[290px]': $store.sidebar.isExpanded || $store.sidebar.isHovered,
|
||||
'xl:ml-[90px]': !$store.sidebar.isExpanded && !$store.sidebar.isHovered,
|
||||
'ml-0': $store.sidebar.isMobileOpen
|
||||
}">
|
||||
<!-- app header start -->
|
||||
@include('layouts.app-header')
|
||||
<!-- app header end -->
|
||||
<div class="p-4 mx-auto max-w-(--breakpoint-2xl) md:p-6">
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@stack('scripts')
|
||||
|
||||
</html>
|
||||
12
resources/views/layouts/backdrop.blade.php
Normal file
12
resources/views/layouts/backdrop.blade.php
Normal file
@@ -0,0 +1,12 @@
|
||||
{{-- <div
|
||||
x-show="$store.sidebar.isMobileOpen"
|
||||
@click="$store.sidebar.toggleMobileOpen()"
|
||||
class="fixed inset-0 bg-gray-900/50 z-[9999] xl:hidden"
|
||||
>
|
||||
sidebarToggle ? 'block xl:hidden' : 'hidden'
|
||||
</div> --}}
|
||||
|
||||
<div
|
||||
:class="$store.sidebar.isMobileOpen ? 'block xl:hidden' : 'hidden'"
|
||||
class="fixed z-50 h-screen w-full bg-gray-900/50"
|
||||
></div>
|
||||
120
resources/views/layouts/fullscreen-layout.blade.php
Normal file
120
resources/views/layouts/fullscreen-layout.blade.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="h-full">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ $title ?? 'Dashboard' }} | {{ config('app.name') }}</title>
|
||||
|
||||
<!-- Scripts -->
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
|
||||
<!-- Alpine.js -->
|
||||
{{-- <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script> --}}
|
||||
|
||||
<!-- Theme Store -->
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('theme', {
|
||||
init() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' :
|
||||
'light';
|
||||
this.theme = savedTheme || systemTheme;
|
||||
this.updateTheme();
|
||||
},
|
||||
theme: 'light',
|
||||
toggle() {
|
||||
this.theme = this.theme === 'light' ? 'dark' : 'light';
|
||||
localStorage.setItem('theme', this.theme);
|
||||
this.updateTheme();
|
||||
},
|
||||
updateTheme() {
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
if (this.theme === 'dark') {
|
||||
html.classList.add('dark');
|
||||
body.classList.add('dark', 'bg-gray-900');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
body.classList.remove('dark', 'bg-gray-900');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Alpine.store('sidebar', {
|
||||
// Initialize based on screen size
|
||||
isExpanded: window.innerWidth >= 1280, // true for desktop, false for mobile
|
||||
isMobileOpen: false,
|
||||
isHovered: false,
|
||||
|
||||
toggleExpanded() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
// When toggling desktop sidebar, ensure mobile menu is closed
|
||||
this.isMobileOpen = false;
|
||||
},
|
||||
|
||||
toggleMobileOpen() {
|
||||
this.isMobileOpen = !this.isMobileOpen;
|
||||
// Don't modify isExpanded when toggling mobile menu
|
||||
},
|
||||
|
||||
setMobileOpen(val) {
|
||||
this.isMobileOpen = val;
|
||||
},
|
||||
|
||||
setHovered(val) {
|
||||
// Only allow hover effects on desktop when sidebar is collapsed
|
||||
if (window.innerWidth >= 1280 && !this.isExpanded) {
|
||||
this.isHovered = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Apply dark mode immediately to prevent flash -->
|
||||
<script>
|
||||
(function() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
const theme = savedTheme || systemTheme;
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.body.classList.add('dark', 'bg-gray-900');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.body.classList.remove('dark', 'bg-gray-900');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body x-data="{ 'loaded': true}" x-init="$store.sidebar.isExpanded = window.innerWidth >= 1280;
|
||||
const checkMobile = () => {
|
||||
if (window.innerWidth < 1280) {
|
||||
$store.sidebar.setMobileOpen(false);
|
||||
$store.sidebar.isExpanded = false;
|
||||
} else {
|
||||
$store.sidebar.isMobileOpen = false;
|
||||
$store.sidebar.isExpanded = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener('resize', checkMobile);">
|
||||
|
||||
{{-- Flash Message Component --}}
|
||||
<x-ui.flash-message />
|
||||
|
||||
{{-- preloader --}}
|
||||
<x-common.preloader/>
|
||||
{{-- preloader end --}}
|
||||
|
||||
@yield('content')
|
||||
|
||||
</body>
|
||||
|
||||
@stack('scripts')
|
||||
|
||||
</html>
|
||||
12
resources/views/layouts/sidebar-widget.blade.php
Normal file
12
resources/views/layouts/sidebar-widget.blade.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="mx-auto mb-10 w-full max-w-60 rounded-2xl bg-gray-50 px-4 py-5 text-center dark:bg-white/[0.03]">
|
||||
<h3 class="mb-2 font-semibold text-gray-900 dark:text-white">
|
||||
#1 Tailwind CSS Dashboard
|
||||
</h3>
|
||||
<p class="mb-4 text-gray-500 text-theme-sm dark:text-gray-400">
|
||||
Leading Tailwind CSS Admin Template with 500+ UI Component and Pages.
|
||||
</p>
|
||||
<a href="https://tailadmin.com/pricing" target="_blank" rel="nofollow"
|
||||
class="flex items-center justify-center p-3 font-medium text-white rounded-lg bg-brand-500 text-theme-sm hover:bg-brand-600">
|
||||
Purchase Plan
|
||||
</a>
|
||||
</div>
|
||||
217
resources/views/layouts/sidebar.blade.php
Normal file
217
resources/views/layouts/sidebar.blade.php
Normal file
@@ -0,0 +1,217 @@
|
||||
|
||||
@php
|
||||
use App\Helpers\MenuHelper;
|
||||
$menuGroups = MenuHelper::getMenuGroups();
|
||||
|
||||
// Get current path
|
||||
$currentPath = request()->path();
|
||||
@endphp
|
||||
|
||||
<aside id="sidebar"
|
||||
class="fixed flex flex-col mt-0 top-0 px-5 left-0 bg-white dark:bg-gray-900 dark:border-gray-800 text-gray-900 h-screen transition-all duration-300 ease-in-out z-99999 border-r border-gray-200"
|
||||
x-data="{
|
||||
openSubmenus: {},
|
||||
init() {
|
||||
// Auto-open Dashboard menu on page load
|
||||
this.initializeActiveMenus();
|
||||
},
|
||||
initializeActiveMenus() {
|
||||
@foreach ($menuGroups as $groupIndex => $menuGroup)
|
||||
@foreach ($menuGroup['items'] as $itemIndex => $item)
|
||||
@if (isset($item['subItems']) && MenuHelper::isActive($item))
|
||||
this.openSubmenus['{{ $groupIndex }}-{{ $itemIndex }}'] = true;
|
||||
@endif
|
||||
@endforeach
|
||||
@endforeach
|
||||
},
|
||||
toggleSubmenu(groupIndex, itemIndex) {
|
||||
const key = groupIndex + '-' + itemIndex;
|
||||
const newState = !this.openSubmenus[key];
|
||||
|
||||
// Close all other submenus when opening a new one
|
||||
if (newState) {
|
||||
this.openSubmenus = {};
|
||||
}
|
||||
|
||||
this.openSubmenus[key] = newState;
|
||||
},
|
||||
isSubmenuOpen(groupIndex, itemIndex) {
|
||||
const key = groupIndex + '-' + itemIndex;
|
||||
return this.openSubmenus[key] || false;
|
||||
},
|
||||
isActive(itemName) {
|
||||
// This is still needed for Alpine, but we can also use PHP to set active classes
|
||||
return false;
|
||||
}
|
||||
}"
|
||||
:class="{
|
||||
'w-[290px]': $store.sidebar.isExpanded || $store.sidebar.isMobileOpen || $store.sidebar.isHovered,
|
||||
'w-[90px]': !$store.sidebar.isExpanded && !$store.sidebar.isHovered,
|
||||
'translate-x-0': $store.sidebar.isMobileOpen,
|
||||
'-translate-x-full xl:translate-x-0': !$store.sidebar.isMobileOpen
|
||||
}"
|
||||
@mouseenter="if (!$store.sidebar.isExpanded) $store.sidebar.setHovered(true)"
|
||||
@mouseleave="$store.sidebar.setHovered(false)">
|
||||
<!-- Logo Section -->
|
||||
<div class="pt-8 pb-7 flex"
|
||||
:class="(!$store.sidebar.isExpanded && !$store.sidebar.isHovered && !$store.sidebar.isMobileOpen) ?
|
||||
'xl:justify-center' :
|
||||
'justify-start'">
|
||||
<a href="/">
|
||||
<img x-show="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen"
|
||||
class="dark:hidden" src="/images/logo/logo.svg" alt="Logo" width="150" height="40" />
|
||||
<img x-show="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen"
|
||||
class="hidden dark:block" src="/images/logo/logo-dark.svg" alt="Logo" width="150"
|
||||
height="40" />
|
||||
<img x-show="!$store.sidebar.isExpanded && !$store.sidebar.isHovered && !$store.sidebar.isMobileOpen"
|
||||
src="/images/logo/logo-icon.svg" alt="Logo" width="32" height="32" />
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Menu -->
|
||||
<div class="flex flex-col overflow-y-auto duration-300 ease-linear no-scrollbar">
|
||||
<nav class="mb-6">
|
||||
<div class="flex flex-col gap-4">
|
||||
@foreach ($menuGroups as $groupIndex => $menuGroup)
|
||||
<div>
|
||||
<!-- Menu Group Title -->
|
||||
<h2 class="mb-4 text-xs uppercase flex leading-[20px] text-gray-400"
|
||||
:class="(!$store.sidebar.isExpanded && !$store.sidebar.isHovered && !$store.sidebar.isMobileOpen) ?
|
||||
'lg:justify-center' : 'justify-start'">
|
||||
<template
|
||||
x-if="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen">
|
||||
<span>{{ $menuGroup['title'] }}</span>
|
||||
</template>
|
||||
<template x-if="!$store.sidebar.isExpanded && !$store.sidebar.isHovered && !$store.sidebar.isMobileOpen">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.99915 10.2451C6.96564 10.2451 7.74915 11.0286 7.74915 11.9951V12.0051C7.74915 12.9716 6.96564 13.7551 5.99915 13.7551C5.03265 13.7551 4.24915 12.9716 4.24915 12.0051V11.9951C4.24915 11.0286 5.03265 10.2451 5.99915 10.2451ZM17.9991 10.2451C18.9656 10.2451 19.7491 11.0286 19.7491 11.9951V12.0051C19.7491 12.9716 18.9656 13.7551 17.9991 13.7551C17.0326 13.7551 16.2491 12.9716 16.2491 12.0051V11.9951C16.2491 11.0286 17.0326 10.2451 17.9991 10.2451ZM13.7491 11.9951C13.7491 11.0286 12.9656 10.2451 11.9991 10.2451C11.0326 10.2451 10.2491 11.0286 10.2491 11.9951V12.0051C10.2491 12.9716 11.0326 13.7551 11.9991 13.7551C12.9656 13.7551 13.7491 12.9716 13.7491 12.0051V11.9951Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</template>
|
||||
</h2>
|
||||
|
||||
<!-- Menu Items -->
|
||||
<ul class="flex flex-col gap-1">
|
||||
@foreach ($menuGroup['items'] as $itemIndex => $item)
|
||||
<li>
|
||||
@if (isset($item['subItems']))
|
||||
<!-- Menu Item with Submenu -->
|
||||
<button @click="toggleSubmenu({{ $groupIndex }}, {{ $itemIndex }})"
|
||||
class="menu-item group w-full"
|
||||
:class="[
|
||||
isSubmenuOpen({{ $groupIndex }}, {{ $itemIndex }}) ?
|
||||
'menu-item-active' : 'menu-item-inactive',
|
||||
!$store.sidebar.isExpanded && !$store.sidebar.isHovered ?
|
||||
'xl:justify-center' : 'xl:justify-start'
|
||||
]">
|
||||
|
||||
<!-- Icon -->
|
||||
<span :class="isSubmenuOpen({{ $groupIndex }}, {{ $itemIndex }}) ?
|
||||
'menu-item-icon-active' : 'menu-item-icon-inactive'">
|
||||
{!! MenuHelper::getIconSvg($item['icon']) !!}
|
||||
</span>
|
||||
|
||||
<!-- Text -->
|
||||
<span
|
||||
x-show="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen"
|
||||
class="menu-item-text flex items-center gap-2">
|
||||
{{ $item['name'] }}
|
||||
@if (!empty($item['new']))
|
||||
<span class="absolute right-10"
|
||||
:class="{{ MenuHelper::isActive($item) ? 'true' : 'false' }} ?
|
||||
'menu-dropdown-badge menu-dropdown-badge-active' :
|
||||
'menu-dropdown-badge menu-dropdown-badge-inactive'">
|
||||
new
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
|
||||
<!-- Chevron Down Icon -->
|
||||
<svg x-show="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen"
|
||||
class="ml-auto w-5 h-5 transition-transform duration-200"
|
||||
:class="{
|
||||
'rotate-180 text-brand-500': isSubmenuOpen({{ $groupIndex }},
|
||||
{{ $itemIndex }})
|
||||
}"
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Submenu -->
|
||||
<div x-show="isSubmenuOpen({{ $groupIndex }}, {{ $itemIndex }}) && ($store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen)">
|
||||
<ul class="mt-2 space-y-1 ml-9">
|
||||
@foreach ($item['subItems'] as $subItem)
|
||||
<li>
|
||||
<a href="{{ isset($subItem['route_name']) ? route($subItem['route_name']) : '#' }}"
|
||||
class="menu-dropdown-item {{ MenuHelper::isActive($subItem) ? 'menu-dropdown-item-active' : 'menu-dropdown-item-inactive' }}">
|
||||
{{ $subItem['name'] }}
|
||||
<span class="flex items-center gap-1 ml-auto">
|
||||
@if (!empty($subItem['new']))
|
||||
<span
|
||||
:class="{{ MenuHelper::isActive($subItem) ? 'true' : 'false' }} ?
|
||||
'menu-dropdown-badge menu-dropdown-badge-active' :
|
||||
'menu-dropdown-badge menu-dropdown-badge-inactive'">
|
||||
new
|
||||
</span>
|
||||
@endif
|
||||
@if (!empty($subItem['pro']))
|
||||
<span
|
||||
:class="{{ MenuHelper::isActive($subItem) ? 'true' : 'false' }} ?
|
||||
'menu-dropdown-badge-pro menu-dropdown-badge-pro-active' :
|
||||
'menu-dropdown-badge-pro menu-dropdown-badge-pro-inactive'">
|
||||
pro
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@else
|
||||
<!-- Simple Menu Item -->
|
||||
<a href="{{ isset($item['route_name']) ? route($item['route_name']) : '#' }}"
|
||||
class="menu-item group {{ MenuHelper::isActive($item) ? 'menu-item-active' : 'menu-item-inactive' }}"
|
||||
:class="(!$store.sidebar.isExpanded && !$store.sidebar.isHovered && !$store.sidebar.isMobileOpen) ? 'xl:justify-center' : 'justify-start'">
|
||||
|
||||
<!-- Icon -->
|
||||
<span
|
||||
:class="{{ MenuHelper::isActive($item) ? 'true' : 'false' }} ? 'menu-item-icon-active' :
|
||||
'menu-item-icon-inactive'">
|
||||
{!! MenuHelper::getIconSvg($item['icon']) !!}
|
||||
</span>
|
||||
|
||||
<!-- Text -->
|
||||
<span
|
||||
x-show="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen"
|
||||
class="menu-item-text flex items-center gap-2">
|
||||
{{ $item['name'] }}
|
||||
@if (!empty($item['new']))
|
||||
<span
|
||||
class="ml-2 inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-brand-500 text-white">
|
||||
new
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
</a>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Sidebar Widget -->
|
||||
<!-- <div x-data x-show="$store.sidebar.isExpanded || $store.sidebar.isHovered || $store.sidebar.isMobileOpen" x-transition class="mt-auto">
|
||||
@include('layouts.sidebar-widget')
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Mobile Overlay -->
|
||||
<div x-show="$store.sidebar.isMobileOpen" @click="$store.sidebar.setMobileOpen(false)"
|
||||
class="fixed z-50 h-screen w-full bg-gray-900/50"></div>
|
||||
72
resources/views/pages/admin/root-ca/index.blade.php
Normal file
72
resources/views/pages/admin/root-ca/index.blade.php
Normal file
@@ -0,0 +1,72 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="sm:flex sm:items-center sm:justify-between mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Root CA Management</h1>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Manage your Root and Intermediate Certificates.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 shadow-theme-xs rounded-xl border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">Type</th>
|
||||
<th scope="col" class="px-6 py-3">Common Name</th>
|
||||
<th scope="col" class="px-6 py-3">Serial Number</th>
|
||||
<th scope="col" class="px-6 py-3">Valid From</th>
|
||||
<th scope="col" class="px-6 py-3">Valid To</th>
|
||||
<th scope="col" class="px-6 py-3">Status</th>
|
||||
<th scope="col" class="px-6 py-3">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($certificates as $cert)
|
||||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{{ ucfirst(str_replace('_', ' ', $cert->ca_type)) }}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
{{ $cert->common_name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 font-mono text-xs">
|
||||
{{ $cert->serial_number }}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
{{ \Carbon\Carbon::parse($cert->valid_from)->format('Y-m-d H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
{{ \Carbon\Carbon::parse($cert->valid_to)->format('Y-m-d H:i') }}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
@if($cert->status === 'valid')
|
||||
<span class="bg-green-100 text-green-800 text-xs font-medium mr-2 px-2.5 py-0.5 rounded dark:bg-green-900 dark:text-green-300">Valid</span>
|
||||
@else
|
||||
<span class="bg-red-100 text-red-800 text-xs font-medium mr-2 px-2.5 py-0.5 rounded dark:bg-red-900 dark:text-red-300">Expired</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<form action="{{ route('admin.root-ca.renew', $cert->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to renew this certificate?');">
|
||||
@csrf
|
||||
<input type="hidden" name="days" value="3650">
|
||||
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-xs px-3 py-1.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
|
||||
Renew (10 Years)
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
||||
<td colspan="7" class="px-6 py-4 text-center">
|
||||
No Root CA certificates found.
|
||||
<span class="text-gray-400">(Run Setup first)</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
213
resources/views/pages/admin/users/index.blade.php
Normal file
213
resources/views/pages/admin/users/index.blade.php
Normal file
@@ -0,0 +1,213 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="mx-auto max-w-(--breakpoint-2xl) p-4 md:p-6" x-data="{
|
||||
showEditEmailModal: false,
|
||||
selectedUserId: null,
|
||||
selectedUserEmail: '',
|
||||
editEmailUrl: '',
|
||||
openEditEmailModal(userId, userEmail) {
|
||||
this.selectedUserId = userId;
|
||||
this.selectedUserEmail = userEmail;
|
||||
this.editEmailUrl = '{{ route('admin.users.update-email', ':id') }}'.replace(':id', userId);
|
||||
this.showEditEmailModal = true;
|
||||
$nextTick(() => $refs.emailInput.focus());
|
||||
}
|
||||
}">
|
||||
<!-- Breadcrumb -->
|
||||
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<h2 class="text-title-md2 font-semibold text-black dark:text-white">
|
||||
User Management
|
||||
</h2>
|
||||
|
||||
<nav>
|
||||
<ol class="flex items-center gap-2">
|
||||
<li>
|
||||
<a class="font-medium text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500"
|
||||
href="{{ route('dashboard') }}">
|
||||
Dashboard /
|
||||
</a>
|
||||
</li>
|
||||
<li class="font-medium text-brand-500">Users</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Table Section -->
|
||||
<div class="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div class="max-w-full overflow-x-auto custom-scrollbar">
|
||||
<table class="w-full min-w-[1102px]">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
<tr class="border-b border-gray-100 dark:border-gray-800">
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
User
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Role
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Joined Date
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Status
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-5 py-3 text-left sm:px-6">
|
||||
<p class="font-medium text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
Actions
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
|
||||
@foreach ($users as $user)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-white/[0.02]">
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center text-gray-500">
|
||||
@if($user->avatar)
|
||||
<img src="{{ asset('storage/' . $user->avatar) }}" alt="{{ $user->name }}" class="w-full h-full object-cover">
|
||||
@else
|
||||
<span class="text-xs font-bold">{{ substr($user->first_name ?? $user->name, 0, 2) }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<div>
|
||||
<span class="block font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
||||
{{ $user->name ?? $user->first_name . ' ' . $user->last_name }}
|
||||
</span>
|
||||
<span class="block text-gray-500 text-theme-xs dark:text-gray-400">
|
||||
{{ $user->email }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {{ $user->isAdmin() ? 'bg-brand-50 text-brand-700 dark:bg-brand-500/15 dark:text-brand-500' : 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300' }}">
|
||||
{{ $user->role ? ucfirst($user->role->name) : 'User' }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<p class="text-gray-500 text-theme-sm dark:text-gray-400">
|
||||
{{ $user->created_at->format('M d, Y') }}
|
||||
<span class="block text-theme-xs text-gray-400">{{ $user->created_at->diffForHumans() }}</span>
|
||||
</p>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium w-fit {{ $user->status === 'active' ? 'bg-green-50 text-green-700 dark:bg-green-500/15 dark:text-green-500' : 'bg-red-50 text-red-700 dark:bg-red-500/15 dark:text-red-500' }}">
|
||||
{{ ucfirst($user->status) }}
|
||||
</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium w-fit {{ $user->hasVerifiedEmail() ? 'bg-blue-50 text-blue-700 dark:bg-blue-500/15 dark:text-blue-500' : 'bg-yellow-50 text-yellow-700 dark:bg-yellow-500/15 dark:text-yellow-500' }}">
|
||||
{{ $user->hasVerifiedEmail() ? 'Verified' : 'Unverified' }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 sm:px-6">
|
||||
<div class="flex items-center space-x-2">
|
||||
@if($user->id !== auth()->id())
|
||||
<form action="{{ route('admin.users.toggle-status', $user->id) }}" method="POST" class="inline-block">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button type="submit" class="text-gray-500 hover:text-{{ $user->status === 'active' ? 'red' : 'green' }}-500 dark:text-gray-400 dark:hover:text-{{ $user->status === 'active' ? 'red' : 'green' }}-500" title="{{ $user->status === 'active' ? 'Suspend' : 'Activate' }} User">
|
||||
@if($user->status === 'active')
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"></path></svg>
|
||||
@else
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
||||
@endif
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<button @click="openEditEmailModal('{{ $user->id }}', '{{ $user->email }}')" class="text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500" title="Edit Email">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg>
|
||||
</button>
|
||||
@endif
|
||||
|
||||
@if(!$user->hasVerifiedEmail())
|
||||
<form action="{{ route('admin.users.send-verification', $user->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Send verification email to {{ $user->email }}?');">
|
||||
@csrf
|
||||
<button type="submit" class="text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500" title="Send Verification Email">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.users.send-reset-link', $user->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Send password reset link to {{ $user->email }}?');">
|
||||
@csrf
|
||||
<button type="submit" class="text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500" title="Send Password Reset Link">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11.536 11 9 13.536 7.464 12 4.929 14.536V17h2.472l4.243-4.243a6 6 0 018.828-5.743zM16.5 13.5V18h6v-4.5h-6z"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@if($user->id !== auth()->id())
|
||||
<form action="{{ route('admin.users.destroy', $user->id) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this user? This action cannot be undone.');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-gray-500 hover:text-red-500 dark:text-gray-400 dark:hover:text-red-500" title="Delete User">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
@if($users->hasPages())
|
||||
<div class="border-t border-gray-200 px-5 py-4 dark:border-gray-800">
|
||||
{{ $users->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Edit Email Modal -->
|
||||
<div x-show="showEditEmailModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-xs"
|
||||
x-transition.opacity style="display: none;">
|
||||
<div @click.outside="showEditEmailModal = false"
|
||||
class="w-full max-w-lg rounded-2xl bg-white p-6 shadow-xl dark:bg-gray-900 mx-4"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95">
|
||||
|
||||
<h3 class="mb-4 text-lg font-semibold text-gray-800 dark:text-white/90">Edit User Email</h3>
|
||||
|
||||
<form :action="editEmailUrl" method="POST">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email Address
|
||||
</label>
|
||||
<input type="email" name="email" x-model="selectedUserEmail" x-ref="emailInput" required
|
||||
class="w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-white/[0.03] dark:text-white/90">
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-3">
|
||||
<button type="button" @click="showEditEmailModal = false"
|
||||
class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-white/[0.03]">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="rounded-lg bg-brand-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-brand-600 focus:outline-hidden focus:ring-2 focus:ring-brand-500/50">
|
||||
Update Email
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
97
resources/views/pages/auth/forgot-password.blade.php
Normal file
97
resources/views/pages/auth/forgot-password.blade.php
Normal file
@@ -0,0 +1,97 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
<div class="relative z-1 bg-white p-6 sm:p-0 dark:bg-gray-900">
|
||||
<div class="relative flex h-screen w-full flex-col justify-center sm:p-0 lg:flex-row dark:bg-gray-900">
|
||||
<!-- Form -->
|
||||
<div class="flex w-full flex-1 flex-col lg:w-1/2">
|
||||
<div class="mx-auto w-full max-w-md pt-10">
|
||||
<a href="{{ route('home') }}"
|
||||
class="inline-flex items-center text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<svg class="stroke-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none">
|
||||
<path d="M12.7083 5L7.5 10.2083L12.7083 15.4167" stroke="" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
Back to Home
|
||||
</a>
|
||||
</div>
|
||||
<div class="mx-auto flex w-full max-w-md flex-1 flex-col justify-center">
|
||||
<div>
|
||||
<div class="mb-5 sm:mb-8">
|
||||
<h1 class="text-title-sm sm:text-title-md mb-2 font-semibold text-gray-800 dark:text-white/90">
|
||||
Forgot Password
|
||||
</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Enter your email address below. We'll look for your account and send you a password reset email.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@if (session('status'))
|
||||
<div class="mb-4 rounded-lg bg-success-50 px-4 py-3 text-sm text-success-600 dark:bg-success-500/10 dark:text-success-500" role="alert">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('password.email') }}" method="POST">
|
||||
@csrf
|
||||
<div class="space-y-5">
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<input type="email" name="email" value="{{ old('email') }}"
|
||||
placeholder="Enter your email" required
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
@error('email')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="bg-brand-500 shadow-theme-xs hover:bg-brand-600 flex w-full items-center justify-center rounded-lg px-4 py-3 text-sm font-medium text-white transition">
|
||||
Send Reset Link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-5 text-center">
|
||||
<a href="{{ route('signin') }}" class="text-brand-500 hover:text-brand-600 dark:text-brand-400 text-sm">
|
||||
Back to Login
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-brand-950 relative hidden h-full w-full items-center lg:grid lg:w-1/2 dark:bg-white/5">
|
||||
<div class="z-1 flex items-center justify-center">
|
||||
<x-common.common-grid-shape />
|
||||
<div class="flex max-w-xs flex-col items-center">
|
||||
<a href="{{ route('home') }}" class="mb-4 block">
|
||||
<img src="{{ asset('images/logo/auth-logo.svg') }}" alt="Logo" />
|
||||
</a>
|
||||
<p class="text-center text-gray-400 dark:text-white/60">
|
||||
Free and Open-Source Tailwind CSS Admin Dashboard Template
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toggler -->
|
||||
<div class="fixed right-6 bottom-6 z-50">
|
||||
<button
|
||||
class="bg-brand-500 hover:bg-brand-600 inline-flex size-14 items-center justify-center rounded-full text-white transition-colors"
|
||||
@click.prevent="$store.theme.toggle()">
|
||||
<!-- Sun Icon -->
|
||||
<svg class="hidden fill-current dark:block" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z" fill="" /> </svg>
|
||||
<!-- Moon Icon -->
|
||||
<svg class="fill-current dark:hidden" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z" fill="" /> </svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
129
resources/views/pages/auth/reset-password.blade.php
Normal file
129
resources/views/pages/auth/reset-password.blade.php
Normal file
@@ -0,0 +1,129 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
<div class="relative z-1 bg-white p-6 sm:p-0 dark:bg-gray-900">
|
||||
<div class="relative flex h-screen w-full flex-col justify-center sm:p-0 lg:flex-row dark:bg-gray-900">
|
||||
<!-- Form -->
|
||||
<div class="flex w-full flex-1 flex-col lg:w-1/2">
|
||||
<div class="mx-auto w-full max-w-md pt-10">
|
||||
<a href="{{ route('home') }}"
|
||||
class="inline-flex items-center text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<svg class="stroke-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none">
|
||||
<path d="M12.7083 5L7.5 10.2083L12.7083 15.4167" stroke="" stroke-width="1.5"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
Back to Home
|
||||
</a>
|
||||
</div>
|
||||
<div class="mx-auto flex w-full max-w-md flex-1 flex-col justify-center">
|
||||
<div>
|
||||
<div class="mb-5 sm:mb-8">
|
||||
<h1 class="text-title-sm sm:text-title-md mb-2 font-semibold text-gray-800 dark:text-white/90">
|
||||
Reset Password
|
||||
</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Enter your new password details below.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('password.update') }}" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="token" value="{{ $token }}">
|
||||
|
||||
<div class="space-y-5">
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<input type="email" name="email" value="{{ $email ?? old('email') }}"
|
||||
placeholder="Your email" required readonly
|
||||
class="cursor-not-allowed bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:placeholder:text-white/30" />
|
||||
@error('email')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
New Password<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<div x-data="{ showPassword: false }" class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" name="password" required
|
||||
placeholder="Enter new password"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span @click="showPassword = !showPassword"
|
||||
class="absolute top-1/2 right-4 z-30 -translate-y-1/2 cursor-pointer text-gray-500 dark:text-gray-400">
|
||||
<svg x-show="!showPassword" class="fill-current" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z"
|
||||
fill="#98A2B3" />
|
||||
</svg>
|
||||
<svg x-show="showPassword" class="fill-current" width="20" height="20"
|
||||
viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M4.63803 3.57709C4.34513 3.2842 3.87026 3.2842 3.57737 3.57709C3.28447 3.86999 3.28447 4.34486 3.57737 4.63775L4.85323 5.91362C3.74609 6.84199 2.89363 8.06395 2.4155 9.45936C2.3615 9.61694 2.3615 9.78801 2.41549 9.94558C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C11.255 15.3619 12.4422 15.0737 13.4994 14.5598L15.3625 16.4229C15.6554 16.7158 16.1302 16.7158 16.4231 16.4229C16.716 16.13 16.716 15.6551 16.4231 15.3622L4.63803 3.57709ZM12.3608 13.4212L10.4475 11.5079C10.3061 11.5423 10.1584 11.5606 10.0064 11.5606H9.99151C8.96527 11.5606 8.13333 10.7286 8.13333 9.70237C8.13333 9.5461 8.15262 9.39434 8.18895 9.24933L5.91885 6.97923C5.03505 7.69015 4.34057 8.62704 3.92328 9.70247C4.86803 12.1373 7.23361 13.8619 10.0002 13.8619C10.8326 13.8619 11.6287 13.7058 12.3608 13.4212ZM16.0771 9.70249C15.7843 10.4569 15.3552 11.1432 14.8199 11.7311L15.8813 12.7925C16.6329 11.9813 17.2187 11.0143 17.5849 9.94561C17.6389 9.78803 17.6389 9.61696 17.5849 9.45938C16.5055 6.30925 13.5184 4.04303 10.0002 4.04303C9.13525 4.04303 8.30244 4.17999 7.52218 4.43338L8.75139 5.66259C9.1556 5.58413 9.57311 5.54303 10.0002 5.54303C12.7667 5.54303 15.1323 7.26768 16.0771 9.70249Z"
|
||||
fill="#98A2B3" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
@error('password')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Confirm Password
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input type="password" name="password_confirmation" required
|
||||
placeholder="Confirm new password"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="bg-brand-500 shadow-theme-xs hover:bg-brand-600 flex w-full items-center justify-center rounded-lg px-4 py-3 text-sm font-medium text-white transition">
|
||||
Reset Password
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-brand-950 relative hidden h-full w-full items-center lg:grid lg:w-1/2 dark:bg-white/5">
|
||||
<div class="z-1 flex items-center justify-center">
|
||||
<x-common.common-grid-shape />
|
||||
<div class="flex max-w-xs flex-col items-center">
|
||||
<a href="{{ route('home') }}" class="mb-4 block">
|
||||
<img src="{{ asset('images/logo/auth-logo.svg') }}" alt="Logo" />
|
||||
</a>
|
||||
<p class="text-center text-gray-400 dark:text-white/60">
|
||||
Free and Open-Source Tailwind CSS Admin Dashboard Template
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toggler -->
|
||||
<div class="fixed right-6 bottom-6 z-50">
|
||||
<button
|
||||
class="bg-brand-500 hover:bg-brand-600 inline-flex size-14 items-center justify-center rounded-full text-white transition-colors"
|
||||
@click.prevent="$store.theme.toggle()">
|
||||
<!-- Sun Icon -->
|
||||
<svg class="hidden fill-current dark:block" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z" fill="" /> </svg>
|
||||
<!-- Moon Icon -->
|
||||
<svg class="fill-current dark:hidden" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z" fill="" /> </svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
142
resources/views/pages/auth/setup-password.blade.php
Normal file
142
resources/views/pages/auth/setup-password.blade.php
Normal file
@@ -0,0 +1,142 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
<div class="relative z-1 bg-white p-6 sm:p-0 dark:bg-gray-900">
|
||||
<div class="relative flex h-screen w-full flex-col justify-center sm:p-0 lg:flex-row dark:bg-gray-900">
|
||||
<!-- Form -->
|
||||
<div class="flex w-full flex-1 flex-col lg:w-1/2">
|
||||
<div class="mx-auto flex w-full max-w-md flex-1 flex-col justify-center">
|
||||
<div>
|
||||
<div class="mb-5 sm:mb-8">
|
||||
<h1 class="text-title-sm sm:text-title-md mb-2 font-semibold text-gray-800 dark:text-white/90">
|
||||
Complete Your Registration
|
||||
</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Set a password to secure your account and enable email login.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- User Info Preview -->
|
||||
<div class="mb-6 rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-800 dark:bg-white/[0.03]">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="h-16 w-16 overflow-hidden rounded-full border border-gray-200 dark:border-gray-800">
|
||||
<img src="{{ session('social_signup_avatar') ?? asset('images/user/owner.jpg') }}" alt="avatar" class="h-full w-full object-cover" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ session('social_signup_name') }}</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ session('social_signup_email') }}</p>
|
||||
<span class="mt-1 inline-flex items-center rounded-full bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-700 dark:bg-blue-900/10 dark:text-blue-400">
|
||||
via {{ ucfirst(session('social_signup_provider', 'social')) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="mb-4 rounded-lg bg-error-50 p-4 text-sm text-error-500">
|
||||
<ul class="list-disc list-inside">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('setup-password') }}" method="POST">
|
||||
@csrf
|
||||
<div class="space-y-5">
|
||||
<!-- Name fields removed as they are provided by social account -->
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Password<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<div x-data="{ showPassword: false }" class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" name="password" required
|
||||
placeholder="Enter your password (min. 8 characters)"
|
||||
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span @click="showPassword = !showPassword"
|
||||
class="absolute top-1/2 right-4 z-30 -translate-y-1/2 cursor-pointer text-gray-500 dark:text-gray-400">
|
||||
<svg x-show="!showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
<svg x-show="showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.63803 3.57709C4.34513 3.2842 3.87026 3.2842 3.57737 3.57709C3.28447 3.86999 3.28447 4.34486 3.57737 4.63775L4.85323 5.91362C3.74609 6.84199 2.89363 8.06395 2.4155 9.45936C2.3615 9.61694 2.3615 9.78801 2.41549 9.94558C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C11.255 15.3619 12.4422 15.0737 13.4994 14.5598L15.3625 16.4229C15.6554 16.7158 16.1302 16.7158 16.4231 16.4229C16.716 16.13 16.716 15.6551 16.4231 15.3622L4.63803 3.57709ZM12.3608 13.4212L10.4475 11.5079C10.3061 11.5423 10.1584 11.5606 10.0064 11.5606H9.99151C8.96527 11.5606 8.13333 10.7286 8.13333 9.70237C8.13333 9.5461 8.15262 9.39434 8.18895 9.24933L5.91885 6.97923C5.03505 7.69015 4.34057 8.62704 3.92328 9.70247C4.86803 12.1373 7.23361 13.8619 10.0002 13.8619C10.8326 13.8619 11.6287 13.7058 12.3608 13.4212ZM16.0771 9.70249C15.7843 10.4569 15.3552 11.1432 14.8199 11.7311L15.8813 12.7925C16.6329 11.9813 17.2187 11.0143 17.5849 9.94561C17.6389 9.78803 17.6389 9.61696 17.5849 9.45938C16.5055 6.30925 13.5184 4.04303 10.0002 4.04303C9.13525 4.04303 8.30244 4.17999 7.52218 4.43338L8.75139 5.66259C9.1556 5.58413 9.57311 5.54303 10.0002 5.54303C12.7667 5.54303 15.1323 7.26768 16.0771 9.70249Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Confirm Password<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<div x-data="{ showPassword: false }" class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" name="password_confirmation" required
|
||||
placeholder="Confirm your password"
|
||||
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span @click="showPassword = !showPassword"
|
||||
class="absolute top-1/2 right-4 z-30 -translate-y-1/2 cursor-pointer text-gray-500 dark:text-gray-400">
|
||||
<svg x-show="!showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
<svg x-show="showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.63803 3.57709C4.34513 3.2842 3.87026 3.2842 3.57737 3.57709C3.28447 3.86999 3.28447 4.34486 3.57737 4.63775L4.85323 5.91362C3.74609 6.84199 2.89363 8.06395 2.4155 9.45936C2.3615 9.61694 2.3615 9.78801 2.41549 9.94558C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C11.255 15.3619 12.4422 15.0737 13.4994 14.5598L15.3625 16.4229C15.6554 16.7158 16.1302 16.7158 16.4231 16.4229C16.716 16.13 16.716 15.6551 16.4231 15.3622L4.63803 3.57709ZM12.3608 13.4212L10.4475 11.5079C10.3061 11.5423 10.1584 11.5606 10.0064 11.5606H9.99151C8.96527 11.5606 8.13333 10.7286 8.13333 9.70237C8.13333 9.5461 8.15262 9.39434 8.18895 9.24933L5.91885 6.97923C5.03505 7.69015 4.34057 8.62704 3.92328 9.70247C4.86803 12.1373 7.23361 13.8619 10.0002 13.8619C10.8326 13.8619 11.6287 13.7058 12.3608 13.4212ZM16.0771 9.70249C15.7843 10.4569 15.3552 11.1432 14.8199 11.7311L15.8813 12.7925C16.6329 11.9813 17.2187 11.0143 17.5849 9.94561C17.6389 9.78803 17.6389 9.61696 17.5849 9.45938C16.5055 6.30925 13.5184 4.04303 10.0002 4.04303C9.13525 4.04303 8.30244 4.17999 7.52218 4.43338L8.75139 5.66259C9.1556 5.58413 9.57311 5.54303 10.0002 5.54303C12.7667 5.54303 15.1323 7.26768 16.0771 9.70249Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info Box -->
|
||||
<div class="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-500/30 dark:bg-blue-500/10">
|
||||
<p class="text-xs text-blue-700 dark:text-blue-400">
|
||||
<strong>Why set a password?</strong><br>
|
||||
This allows you to sign in with your email and password in the future, even if you don't want to use {{ ucfirst(session('social_signup_provider', 'social')) }} login.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="bg-brand-500 shadow-theme-xs hover:bg-brand-600 flex w-full items-center justify-center rounded-lg px-4 py-3 text-sm font-medium text-white transition">
|
||||
Complete Registration
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-brand-950 relative hidden h-full w-full items-center lg:grid lg:w-1/2 dark:bg-white/5">
|
||||
<div class="z-1 flex items-center justify-center">
|
||||
<x-common.common-grid-shape />
|
||||
<div class="flex max-w-xs flex-col items-center">
|
||||
<a href="{{ route('home') }}" class="mb-4 block">
|
||||
<img src="./images/logo/auth-logo.svg" alt="Logo" />
|
||||
</a>
|
||||
<p class="text-center text-gray-400 dark:text-white/60">
|
||||
One more step to complete your registration
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Theme Toggler -->
|
||||
<div class="fixed right-6 bottom-6 z-50">
|
||||
<button
|
||||
class="bg-brand-500 hover:bg-brand-600 inline-flex size-14 items-center justify-center rounded-full text-white transition-colors"
|
||||
@click.prevent="$store.theme.toggle()">
|
||||
<svg class="hidden fill-current dark:block" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z" fill="" />
|
||||
</svg>
|
||||
<svg class="fill-current dark:hidden" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z" fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
173
resources/views/pages/auth/signin.blade.php
Normal file
173
resources/views/pages/auth/signin.blade.php
Normal file
@@ -0,0 +1,173 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
<div class="relative z-1 bg-white p-6 sm:p-0 dark:bg-gray-900">
|
||||
<div class="relative flex h-screen w-full flex-col justify-center sm:p-0 lg:flex-row dark:bg-gray-900">
|
||||
<!-- Form -->
|
||||
<div class="flex w-full flex-1 flex-col lg:w-1/2">
|
||||
<div class="mx-auto w-full max-w-md pt-10">
|
||||
<a href="{{ route('dashboard') }}"
|
||||
class="inline-flex items-center text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<svg class="stroke-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M12.7083 5L7.5 10.2083L12.7083 15.4167" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
Back to dashboard
|
||||
</a>
|
||||
</div>
|
||||
<div class="mx-auto flex w-full max-w-md flex-1 flex-col justify-center">
|
||||
<div>
|
||||
<div class="mb-5 sm:mb-8">
|
||||
<h1 class="text-title-sm sm:text-title-md mb-2 font-semibold text-gray-800 dark:text-white/90">
|
||||
Sign In
|
||||
</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Enter your email and password to sign in!
|
||||
</p>
|
||||
</div>
|
||||
<!-- Session Error handled globally -->
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-5">
|
||||
<button onclick="window.location.href = '{{ route('auth.social', ['provider' => 'google', 'context' => 'signin']) }}'"
|
||||
class="inline-flex items-center justify-center gap-3 rounded-lg bg-gray-100 px-7 py-3 text-sm font-normal text-gray-700 transition-colors hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M18.7511 10.1944C18.7511 9.47495 18.6915 8.94995 18.5626 8.40552H10.1797V11.6527H15.1003C15.0011 12.4597 14.4654 13.675 13.2749 14.4916L13.2582 14.6003L15.9087 16.6126L16.0924 16.6305C17.7788 15.1041 18.7511 12.8583 18.7511 10.1944Z"
|
||||
fill="#4285F4" />
|
||||
<path
|
||||
d="M10.1788 18.75C12.5895 18.75 14.6133 17.9722 16.0915 16.6305L13.274 14.4916C12.5201 15.0068 11.5081 15.3666 10.1788 15.3666C7.81773 15.3666 5.81379 13.8402 5.09944 11.7305L4.99473 11.7392L2.23868 13.8295L2.20264 13.9277C3.67087 16.786 6.68674 18.75 10.1788 18.75Z"
|
||||
fill="#34A853" />
|
||||
<path
|
||||
d="M5.10014 11.7305C4.91165 11.186 4.80257 10.6027 4.80257 9.99992C4.80257 9.3971 4.91165 8.81379 5.09022 8.26935L5.08523 8.1534L2.29464 6.02954L2.20333 6.0721C1.5982 7.25823 1.25098 8.5902 1.25098 9.99992C1.25098 11.4096 1.5982 12.7415 2.20333 13.9277L5.10014 11.7305Z"
|
||||
fill="#FBBC05" />
|
||||
<path
|
||||
d="M10.1789 4.63331C11.8554 4.63331 12.9864 5.34303 13.6312 5.93612L16.1511 3.525C14.6035 2.11528 12.5895 1.25 10.1789 1.25C6.68676 1.25 3.67088 3.21387 2.20264 6.07218L5.08953 8.26943C5.81381 6.15972 7.81776 4.63331 10.1789 4.63331Z"
|
||||
fill="#EB4335" />
|
||||
</svg>
|
||||
Sign in with Google
|
||||
</button>
|
||||
<button onclick="window.location.href = '{{ route('auth.social', ['provider' => 'github', 'context' => 'signin']) }}'"
|
||||
class="inline-flex items-center justify-center gap-3 rounded-lg bg-gray-100 px-7 py-3 text-sm font-normal text-gray-700 transition-colors hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10">
|
||||
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
|
||||
Sign in with GitHub
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative py-3 sm:py-5">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-gray-200 dark:border-gray-800"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="bg-white p-2 text-gray-400 sm:px-5 sm:py-2 dark:bg-gray-900">Or</span>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('signin') }}" method="POST">
|
||||
@csrf
|
||||
<div class="space-y-5">
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<input type="email" id="email" name="email" value="{{ old('email') }}" placeholder="info@gmail.com"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
@error('email')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Password<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<div x-data="{ showPassword: false }" class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'"
|
||||
name="password"
|
||||
placeholder="Enter your password"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
<span @click="showPassword = !showPassword"
|
||||
class="absolute top-1/2 right-4 z-30 -translate-y-1/2 cursor-pointer text-gray-500 dark:text-gray-400">
|
||||
<svg x-show="!showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
<svg x-show="showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M4.63803 3.57709C4.34513 3.2842 3.87026 3.2842 3.57737 3.57709C3.28447 3.86999 3.28447 4.34486 3.57737 4.63775L4.85323 5.91362C3.74609 6.84199 2.89363 8.06395 2.4155 9.45936C2.3615 9.61694 2.3615 9.78801 2.41549 9.94558C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C11.255 15.3619 12.4422 15.0737 13.4994 14.5598L15.3625 16.4229C15.6554 16.7158 16.1302 16.7158 16.4231 16.4229C16.716 16.13 16.716 15.6551 16.4231 15.3622L4.63803 3.57709ZM12.3608 13.4212L10.4475 11.5079C10.3061 11.5423 10.1584 11.5606 10.0064 11.5606H9.99151C8.96527 11.5606 8.13333 10.7286 8.13333 9.70237C8.13333 9.5461 8.15262 9.39434 8.18895 9.24933L5.91885 6.97923C5.03505 7.69015 4.34057 8.62704 3.92328 9.70247C4.86803 12.1373 7.23361 13.8619 10.0002 13.8619C10.8326 13.8619 11.6287 13.7058 12.3608 13.4212ZM16.0771 9.70249C15.7843 10.4569 15.3552 11.1432 14.8199 11.7311L15.8813 12.7925C16.6329 11.9813 17.2187 11.0143 17.5849 9.94561C17.6389 9.78803 17.6389 9.61696 17.5849 9.45938C16.5055 6.30925 13.5184 4.04303 10.0002 4.04303C9.13525 4.04303 8.30244 4.17999 7.52218 4.43338L8.75139 5.66259C9.1556 5.58413 9.57311 5.54303 10.0002 5.54303C12.7667 5.54303 15.1323 7.26768 16.0771 9.70249Z"
|
||||
fill="#98A2B3" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Checkbox -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div x-data="{ checkboxToggle: false }">
|
||||
<label for="checkboxLabelOne"
|
||||
class="flex cursor-pointer items-center text-sm font-normal text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="checkboxLabelOne" name="remember" class="sr-only" @change="checkboxToggle = !checkboxToggle" />
|
||||
<div :class="checkboxToggle ? 'border-brand-500 bg-brand-500' :
|
||||
'bg-transparent border-gray-300 dark:border-gray-700'"
|
||||
class="mr-3 flex h-5 w-5 items-center justify-center rounded-md border-[1.25px]">
|
||||
<span :class="checkboxToggle ? '' : 'opacity-0'">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" stroke="white" stroke-width="1.94437" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Keep me logged in
|
||||
</label>
|
||||
</div>
|
||||
<a href="{{ route('password.request') }}" class="text-brand-500 hover:text-brand-600 dark:text-brand-400 text-sm">
|
||||
Forgot password?
|
||||
</a>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="bg-brand-500 shadow-theme-xs hover:bg-brand-600 flex w-full items-center justify-center rounded-lg px-4 py-3 text-sm font-medium text-white transition">
|
||||
Sign In
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="mt-5">
|
||||
<p class="text-center text-sm font-normal text-gray-700 sm:text-start dark:text-gray-400">
|
||||
Don't have an account?
|
||||
<a href="{{ route('signup') }}" class="text-brand-500 hover:text-brand-600 dark:text-brand-400">Sign Up</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-brand-950 relative hidden h-full w-full items-center lg:grid lg:w-1/2 dark:bg-white/5">
|
||||
<div class="z-1 flex items-center justify-center">
|
||||
<!-- ===== Common Grid Shape Start ===== -->
|
||||
<x-common.common-grid-shape/>
|
||||
<div class="flex max-w-xs flex-col items-center">
|
||||
<a href="{{ route('home') }}" class="mb-4 block">
|
||||
<img src="./images/logo/auth-logo.svg" alt="Logo" />
|
||||
</a>
|
||||
<p class="text-center text-gray-400 dark:text-white/60">
|
||||
Free and Open-Source Tailwind CSS Admin Dashboard Template
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toggler -->
|
||||
<div class="fixed right-6 bottom-6 z-50">
|
||||
<button
|
||||
class="bg-brand-500 hover:bg-brand-600 inline-flex size-14 items-center justify-center rounded-full text-white transition-colors"
|
||||
@click.prevent="$store.theme.toggle()">
|
||||
<svg class="hidden fill-current dark:block" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z" fill="" />
|
||||
</svg>
|
||||
<svg class="fill-current dark:hidden" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z" fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
193
resources/views/pages/auth/signup.blade.php
Normal file
193
resources/views/pages/auth/signup.blade.php
Normal file
@@ -0,0 +1,193 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
<div class="relative z-1 bg-white p-6 sm:p-0 dark:bg-gray-900">
|
||||
<div class="flex h-screen w-full flex-col justify-center sm:p-0 lg:flex-row dark:bg-gray-900">
|
||||
<!-- Form -->
|
||||
<div class="flex w-full flex-1 flex-col lg:w-1/2">
|
||||
<div class="mx-auto w-full max-w-md pt-5 sm:py-10">
|
||||
<a href="{{ route('home') }}"
|
||||
class="inline-flex items-center text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
|
||||
<svg class="stroke-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M12.7083 5L7.5 10.2083L12.7083 15.4167" stroke="" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
Back to dashboard
|
||||
</a>
|
||||
</div>
|
||||
<div class="mx-auto flex w-full max-w-md flex-1 flex-col justify-center">
|
||||
<div class="mb-5 sm:mb-8">
|
||||
<h1 class="text-title-sm sm:text-title-md mb-2 font-semibold text-gray-800 dark:text-white/90">
|
||||
Sign Up
|
||||
</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Enter your email and password to sign up!
|
||||
</p>
|
||||
</div>
|
||||
<!-- Session Error handled globally -->
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-5">
|
||||
<button onclick="window.location.href = '{{ route('auth.social', ['provider' => 'google', 'context' => 'signup']) }}'"
|
||||
class="inline-flex items-center justify-center gap-3 rounded-lg bg-gray-100 px-7 py-3 text-sm font-normal text-gray-700 transition-colors hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.7511 10.1944C18.7511 9.47495 18.6915 8.94995 18.5626 8.40552H10.1797V11.6527H15.1003C15.0011 12.4597 14.4654 13.675 13.2749 14.4916L13.2582 14.6003L15.9087 16.6126L16.0924 16.6305C17.7788 15.1041 18.7511 12.8583 18.7511 10.1944Z" fill="#4285F4" />
|
||||
<path d="M10.1788 18.75C12.5895 18.75 14.6133 17.9722 16.0915 16.6305L13.274 14.4916C12.5201 15.0068 11.5081 15.3666 10.1788 15.3666C7.81773 15.3666 5.81379 13.8402 5.09944 11.7305L4.99473 11.7392L2.23868 13.8295L2.20264 13.9277C3.67087 16.786 6.68674 18.75 10.1788 18.75Z" fill="#34A853" />
|
||||
<path d="M5.10014 11.7305C4.91165 11.186 4.80257 10.6027 4.80257 9.99992C4.80257 9.3971 4.91165 8.81379 5.09022 8.26935L5.08523 8.1534L2.29464 6.02954L2.20333 6.0721C1.5982 7.25823 1.25098 8.5902 1.25098 9.99992C1.25098 11.4096 1.5982 12.7415 2.20333 13.9277L5.10014 11.7305Z" fill="#FBBC05" />
|
||||
<path d="M10.1789 4.63331C11.8554 4.63331 12.9864 5.34303 13.6312 5.93612L16.1511 3.525C14.6035 2.11528 12.5895 1.25 10.1789 1.25C6.68676 1.25 3.67088 3.21387 2.20264 6.07218L5.08953 8.26943C5.81381 6.15972 7.81776 4.63331 10.1789 4.63331Z" fill="#EB4335" />
|
||||
</svg>
|
||||
Sign up with Google
|
||||
</button>
|
||||
<button onclick="window.location.href = '{{ route('auth.social', ['provider' => 'github', 'context' => 'signup']) }}'"
|
||||
class="inline-flex items-center justify-center gap-3 rounded-lg bg-gray-100 px-7 py-3 text-sm font-normal text-gray-700 transition-colors hover:bg-gray-200 hover:text-gray-800 dark:bg-white/5 dark:text-white/90 dark:hover:bg-white/10">
|
||||
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
|
||||
Sign up with GitHub
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative py-3 sm:py-5">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-gray-200 dark:border-gray-800"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="bg-white p-2 text-gray-400 sm:px-5 sm:py-2 dark:bg-gray-900">Or</span>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('signup') }}" method="POST">
|
||||
@csrf
|
||||
<div class="space-y-5">
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
<!-- First Name -->
|
||||
<div class="sm:col-span-1">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
First Name<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<input type="text" id="fname" name="fname" value="{{ old('fname') }}"
|
||||
placeholder="Enter your first name"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
@error('fname')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<!-- Last Name -->
|
||||
<div class="sm:col-span-1">
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Last Name <span class="text-gray-400 font-normal">(Optional)</span>
|
||||
</label>
|
||||
<input type="text" id="lname" name="lname" value="{{ old('lname') }}"
|
||||
placeholder="Enter your last name"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
@error('lname')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Email<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<input type="email" id="email" name="email" value="{{ old('email') }}" placeholder="Enter your email"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
@error('email')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Password<span class="text-error-500">*</span>
|
||||
</label>
|
||||
<div x-data="{ showPassword: false }" class="relative">
|
||||
<input :type="showPassword ? 'text' : 'password'" name="password" placeholder="Enter your password"
|
||||
class="dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent py-2.5 pr-11 pl-4 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30" />
|
||||
@error('password')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
<span @click="showPassword = !showPassword"
|
||||
class="absolute top-1/2 right-4 z-30 -translate-y-1/2 cursor-pointer text-gray-500 dark:text-gray-400">
|
||||
<svg x-show="!showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
<svg x-show="showPassword" class="fill-current" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.63803 3.57709C4.34513 3.2842 3.87026 3.2842 3.57737 3.57709C3.28447 3.86999 3.28447 4.34486 3.57737 4.63775L4.85323 5.91362C3.74609 6.84199 2.89363 8.06395 2.4155 9.45936C2.3615 9.61694 2.3615 9.78801 2.41549 9.94558C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C11.255 15.3619 12.4422 15.0737 13.4994 14.5598L15.3625 16.4229C15.6554 16.7158 16.1302 16.7158 16.4231 16.4229C16.716 16.13 16.716 15.6551 16.4231 15.3622L4.63803 3.57709ZM12.3608 13.4212L10.4475 11.5079C10.3061 11.5423 10.1584 11.5606 10.0064 11.5606H9.99151C8.96527 11.5606 8.13333 10.7286 8.13333 9.70237C8.13333 9.5461 8.15262 9.39434 8.18895 9.24933L5.91885 6.97923C5.03505 7.69015 4.34057 8.62704 3.92328 9.70247C4.86803 12.1373 7.23361 13.8619 10.0002 13.8619C10.8326 13.8619 11.6287 13.7058 12.3608 13.4212ZM16.0771 9.70249C15.7843 10.4569 15.3552 11.1432 14.8199 11.7311L15.8813 12.7925C16.6329 11.9813 17.2187 11.0143 17.5849 9.94561C17.6389 9.78803 17.6389 9.61696 17.5849 9.45938C16.5055 6.30925 13.5184 4.04303 10.0002 4.04303C9.13525 4.04303 8.30244 4.17999 7.52218 4.43338L8.75139 5.66259C9.1556 5.58413 9.57311 5.54303 10.0002 5.54303C12.7667 5.54303 15.1323 7.26768 16.0771 9.70249Z" fill="#98A2B3" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Checkbox -->
|
||||
<div>
|
||||
<div x-data="{ checkboxToggle: false }">
|
||||
<label for="checkboxLabelOne"
|
||||
class="flex cursor-pointer items-start text-sm font-normal text-gray-700 select-none dark:text-gray-400">
|
||||
<div class="relative">
|
||||
<input type="checkbox" id="checkboxLabelOne" name="terms" class="sr-only" required @change="checkboxToggle = !checkboxToggle" />
|
||||
<div :class="checkboxToggle ? 'border-brand-500 bg-brand-500' :
|
||||
'bg-transparent border-gray-300 dark:border-gray-700'"
|
||||
class="mr-3 flex h-5 w-5 items-center justify-center rounded-md border-[1.25px]">
|
||||
<span :class="checkboxToggle ? '' : 'opacity-0'">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" stroke="white" stroke-width="1.94437" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="inline-block font-normal text-gray-500 dark:text-gray-400">
|
||||
By creating an account means you agree to the
|
||||
<span class="text-gray-800 dark:text-white/90">
|
||||
Terms and Conditions,
|
||||
</span>
|
||||
and our
|
||||
<span class="text-gray-800 dark:text-white">
|
||||
Privacy Policy
|
||||
</span>
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="bg-brand-500 shadow-theme-xs hover:bg-brand-600 flex w-full items-center justify-center rounded-lg px-4 py-3 text-sm font-medium text-white transition">
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="mt-5">
|
||||
<p class="text-center text-sm font-normal text-gray-700 sm:text-start dark:text-gray-400">
|
||||
Already have an account?
|
||||
<a href="{{ route('signin') }}" class="text-brand-500 hover:text-brand-600 dark:text-brand-400">Sign In</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-brand-950 relative hidden h-full w-full items-center lg:grid lg:w-1/2 dark:bg-white/5">
|
||||
<div class="z-1 flex items-center justify-center">
|
||||
<!-- ===== Common Grid Shape Start ===== -->
|
||||
<x-common.common-grid-shape />
|
||||
<div class="flex max-w-xs flex-col items-center">
|
||||
<a href="{{ route('home') }}" class="mb-4 block">
|
||||
<img src="./images/logo/auth-logo.svg" alt="Logo" />
|
||||
</a>
|
||||
<p class="text-center text-gray-400 dark:text-white/60">
|
||||
Free and Open-Source Tailwind CSS Admin Dashboard Template
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toggler -->
|
||||
<div class="fixed right-6 bottom-6 z-50">
|
||||
<button
|
||||
class="bg-brand-500 hover:bg-brand-600 inline-flex size-14 items-center justify-center rounded-full text-white transition-colors"
|
||||
@click.prevent="$store.theme.toggle()">
|
||||
<svg class="hidden fill-current dark:block" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z" fill="" />
|
||||
</svg>
|
||||
<svg class="fill-current dark:hidden" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z" fill="" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
66
resources/views/pages/auth/verify-email.blade.php
Normal file
66
resources/views/pages/auth/verify-email.blade.php
Normal file
@@ -0,0 +1,66 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
<div class="relative z-1 bg-white p-6 sm:p-0 dark:bg-gray-900">
|
||||
<div class="relative flex h-screen w-full flex-col justify-center sm:p-0 lg:flex-row dark:bg-gray-900">
|
||||
<div class="flex w-full flex-1 flex-col lg:w-1/2">
|
||||
<div class="w-full h-full flex items-center justify-center p-4 sm:p-12.5 xl:p-17.5 text-center">
|
||||
<div class="max-w-md w-full">
|
||||
<h2 class="mb-9 text-2xl font-bold text-black dark:text-white sm:text-title-xl2">
|
||||
Verify Your Email
|
||||
</h2>
|
||||
|
||||
<div class="mb-6">
|
||||
<span class="inline-block p-4 rounded-full bg-brand-50 text-brand-500 dark:bg-brand-500/10 mb-4">
|
||||
<svg width="60" height="60" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22 6C22 4.9 21.1 4 20 4H4C2.9 4 2 4.9 2 6V18C2 19.1 2.9 20 4 20H20C21.1 20 22 19.1 22 18V6ZM20 6L12 11L4 6H20ZM20 18H4V8L12 13L20 8V18Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</span>
|
||||
<p class="text-gray-700 dark:text-gray-400">
|
||||
Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you?
|
||||
</p>
|
||||
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
If you didn't receive the email, we will gladly send you another.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="mb-4 text-sm font-medium text-green-600 dark:text-green-400">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('verification.send') }}">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="w-full cursor-pointer rounded-lg border border-brand-500 bg-brand-500 p-4 text-white transition hover:bg-opacity-90 hover:bg-brand-600">
|
||||
Resend Verification Email
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form action="{{ route('logout') }}" method="POST" class="mt-4">
|
||||
@csrf
|
||||
<button type="submit" class="text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 underline">
|
||||
Log Out
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-brand-950 relative hidden h-full w-full items-center lg:grid lg:w-1/2 dark:bg-white/5">
|
||||
<div class="z-1 flex items-center justify-center">
|
||||
<x-common.common-grid-shape/>
|
||||
<div class="flex max-w-xs flex-col items-center">
|
||||
<a href="/" class="mb-4 block">
|
||||
<img src="{{ asset('images/logo/auth-logo.svg') }}" alt="Logo" />
|
||||
</a>
|
||||
<p class="text-center text-gray-400 dark:text-white/60">
|
||||
Secure Certificate Management System
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
17
resources/views/pages/blank.blade.php
Normal file
17
resources/views/pages/blank.blade.php
Normal file
@@ -0,0 +1,17 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="Blank Page" />
|
||||
<div class="min-h-screen rounded-2xl border border-gray-200 bg-white px-5 py-7 dark:border-gray-800 dark:bg-white/[0.03] xl:px-10 xl:py-12">
|
||||
<div class="mx-auto w-full max-w-[630px] text-center">
|
||||
<h3 class="mb-4 font-semibold text-gray-800 text-theme-xl dark:text-white/90 sm:text-2xl">
|
||||
Card Title Here
|
||||
</h3>
|
||||
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 sm:text-base">
|
||||
Start putting content on grids or panels, you can also use different combinations of
|
||||
grids.Please check out the dashboard and other pages
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
6
resources/views/pages/calender.blade.php
Normal file
6
resources/views/pages/calender.blade.php
Normal file
@@ -0,0 +1,6 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="Calender" />
|
||||
<x-calender-area />
|
||||
@endsection
|
||||
160
resources/views/pages/certificate/create.blade.php
Normal file
160
resources/views/pages/certificate/create.blade.php
Normal file
@@ -0,0 +1,160 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="Generate SSL Certificate" />
|
||||
|
||||
<div class="max-w-3xl mx-auto mb-6">
|
||||
@if(session('error'))
|
||||
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400 border border-red-100 dark:border-red-900" role="alert">
|
||||
<span class="font-medium">Error!</span> {{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="max-w-3xl mx-auto" x-data="{
|
||||
common_name: '',
|
||||
config_mode: 'default',
|
||||
san: '',
|
||||
isValid() {
|
||||
return this.common_name.length > 3 && this.common_name.includes('.');
|
||||
}
|
||||
}">
|
||||
<x-common.component-card>
|
||||
<x-slot:header>
|
||||
<div class="mb-2">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Generate SSL Certificate</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 text-balance">Create a new self-signed certificate using your Local CA. This will be signed by your private root authority.</p>
|
||||
</div>
|
||||
</x-slot:header>
|
||||
|
||||
<form action="{{ route('certificate.generate') }}" method="POST" class="space-y-8"
|
||||
x-data="{
|
||||
common_name: '',
|
||||
config_mode: 'default',
|
||||
key_bits: '2048',
|
||||
san: '',
|
||||
isValid() {
|
||||
return this.common_name.length > 3 && this.common_name.includes('.');
|
||||
}
|
||||
}">
|
||||
@csrf
|
||||
|
||||
<!-- Configuration Mode -->
|
||||
<div>
|
||||
<label class="block mb-3 text-sm font-medium text-gray-700 dark:text-gray-300">Configuration Mode</label>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<label class="relative flex p-4 cursor-pointer rounded-xl border transition-all"
|
||||
:class="config_mode === 'default' ? 'border-brand-500 ring-1 ring-brand-500 bg-brand-50/50 dark:bg-brand-900/10 dark:border-brand-400' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'">
|
||||
<input type="radio" name="config_mode" value="default" x-model="config_mode" class="sr-only">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center justify-center flex-shrink-0 w-5 h-5 mt-0.5 border rounded-full transition-colors"
|
||||
:class="config_mode === 'default' ? 'border-brand-500 bg-brand-500' : 'border-gray-300 dark:border-gray-600'">
|
||||
<div class="w-2 h-2 bg-white rounded-full" x-show="config_mode === 'default'"></div>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-medium text-gray-900 dark:text-white">Default Presets</span>
|
||||
<span class="block mt-0.5 text-xs text-gray-500 dark:text-gray-400">Use system defaults for Organization, Locality, and Country settings.</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="relative flex p-4 cursor-pointer rounded-xl border transition-all"
|
||||
:class="config_mode === 'manual' ? 'border-brand-500 ring-1 ring-brand-500 bg-brand-50/50 dark:bg-brand-900/10 dark:border-brand-400' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'">
|
||||
<input type="radio" name="config_mode" value="manual" x-model="config_mode" class="sr-only">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center justify-center flex-shrink-0 w-5 h-5 mt-0.5 border rounded-full transition-colors"
|
||||
:class="config_mode === 'manual' ? 'border-brand-500 bg-brand-500' : 'border-gray-300 dark:border-gray-600'">
|
||||
<div class="w-2 h-2 bg-white rounded-full" x-show="config_mode === 'manual'"></div>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-medium text-gray-900 dark:text-white">Manual Configuration</span>
|
||||
<span class="block mt-0.5 text-xs text-gray-500 dark:text-gray-400">Customise all Distinguised Name (DN) attributes manually.</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Details -->
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Common Name (Domain) <span class="text-error-500">*</span></label>
|
||||
<input type="text" name="common_name" x-model="common_name" placeholder="e.g. example.com or e.g. 127.0.0.1" required
|
||||
class="w-full px-4 py-2.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white transition-colors"
|
||||
:class="common_name && !isValid() ? 'border-error-500 focus:ring-error-500/20 focus:border-error-500' : ''">
|
||||
<p x-show="common_name && !isValid()" class="mt-1.5 text-xs text-error-500 animate-pulse">Please enter a valid domain name containing at least one dot.</p>
|
||||
<p class="mt-1.5 text-xs text-gray-500">The primary Fully Qualified Domain Name (FQDN) or IP Address to be secured.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Subject Alternative Names (SAN)</label>
|
||||
<input type="text" name="san" x-model="san" placeholder="e.g. api.local, 192.168.1.50"
|
||||
class="w-full px-4 py-2.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white transition-colors">
|
||||
<p class="mt-1.5 text-xs text-gray-500">Optional comma-separated list of additional domains or IPs.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manual Mode Fields -->
|
||||
<div x-show="config_mode === 'manual'" x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2" x-transition:enter-end="opacity-100 translate-y-0"
|
||||
class="p-5 border border-gray-100 rounded-xl bg-gray-50 dark:bg-gray-800/50 dark:border-gray-700/50">
|
||||
|
||||
<h4 class="mb-4 text-xs font-semibold tracking-wider text-gray-500 uppercase font-lexend">Distinguished Name Attributes</h4>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">Organization (O)</label>
|
||||
<input type="text" name="organization" value="{{ $defaults['organizationName'] ?? '' }}"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">Country (C)</label>
|
||||
<input type="text" name="country" value="{{ $defaults['countryName'] ?? 'ID' }}" maxlength="2"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">State / Province (ST)</label>
|
||||
<input type="text" name="state" value="{{ $defaults['stateOrProvinceName'] ?? '' }}"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">Locality (L)</label>
|
||||
<input type="text" name="locality" value="{{ $defaults['localityName'] ?? '' }}"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Key Size -->
|
||||
<div>
|
||||
<label class="block mb-3 text-sm font-medium text-gray-700 dark:text-gray-300">Private Key Size</label>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<label class="cursor-pointer">
|
||||
<input type="radio" name="key_bits" value="2048" x-model="key_bits" class="peer sr-only">
|
||||
<div class="px-4 py-2 text-sm font-medium text-gray-600 bg-white border border-gray-200 rounded-lg peer-checked:bg-brand-50 peer-checked:text-brand-700 peer-checked:border-brand-500 hover:bg-gray-50 transition-all dark:bg-gray-800 dark:border-gray-700 dark:text-gray-300 dark:peer-checked:bg-brand-900/30 dark:peer-checked:text-brand-300 dark:peer-checked:border-brand-500">
|
||||
2048 Bit (Standard)
|
||||
</div>
|
||||
</label>
|
||||
<label class="cursor-pointer">
|
||||
<input type="radio" name="key_bits" value="4096" x-model="key_bits" class="peer sr-only">
|
||||
<div class="px-4 py-2 text-sm font-medium text-gray-600 bg-white border border-gray-200 rounded-lg peer-checked:bg-brand-50 peer-checked:text-brand-700 peer-checked:border-brand-500 hover:bg-gray-50 transition-all dark:bg-gray-800 dark:border-gray-700 dark:text-gray-300 dark:peer-checked:bg-brand-900/30 dark:peer-checked:text-brand-300 dark:peer-checked:border-brand-500">
|
||||
4096 Bit (High Security)
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center justify-end pt-6 space-x-3 border-t border-gray-100 dark:border-gray-800">
|
||||
<a href="{{ route('certificate.index') }}" class="px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700 transition shadow-sm">
|
||||
Cancel
|
||||
</a>
|
||||
<button type="submit" :disabled="!isValid()"
|
||||
class="px-6 py-2.5 text-sm font-semibold text-white transition rounded-lg bg-brand-500 hover:bg-brand-600 shadow-theme-xs disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
Generate Certificate
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</x-common.component-card>
|
||||
</div>
|
||||
@endsection
|
||||
210
resources/views/pages/certificate/index.blade.php
Normal file
210
resources/views/pages/certificate/index.blade.php
Normal file
@@ -0,0 +1,210 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div x-data="{ createOpen: {{ $errors->any() ? 'true' : 'false' }} }">
|
||||
<div class="flex flex-col gap-4 mb-6 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Certificate Management
|
||||
</h2>
|
||||
<nav class="mt-1">
|
||||
<ol class="flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400">
|
||||
<li>
|
||||
<a href="{{ route('dashboard') }}" class="inline-flex items-center gap-1.5 hover:text-brand-500 transition">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg class="stroke-current opacity-60" width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.75 2.5L6.25 5L3.75 7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</li>
|
||||
<li class="font-medium text-gray-800 dark:text-white/90">Certificate Management</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
<div>
|
||||
@if($caReady)
|
||||
<button @click="createOpen = true" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition rounded-lg bg-brand-500 hover:bg-brand-600 shadow-theme-xs">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg>
|
||||
Generate New SSL
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(!$caReady)
|
||||
<div class="p-6 mb-6 border border-yellow-200 rounded-xl bg-yellow-50 dark:bg-yellow-900/10 dark:border-yellow-900/30">
|
||||
<div class="flex items-start">
|
||||
<div class="p-3 bg-yellow-100 rounded-lg dark:bg-yellow-900/30">
|
||||
<svg class="w-6 h-6 text-yellow-600 dark:text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h4 class="text-lg font-bold text-yellow-800 dark:text-yellow-200">Setup Required</h4>
|
||||
<p class="mt-1 text-sm text-yellow-700 dark:text-yellow-300">Root CA and Intermediate CA have not been initialized in the database yet.</p>
|
||||
|
||||
@if(Auth::user()->isAdmin())
|
||||
<form action="{{ route('admin.setup-ca') }}" method="POST" class="mt-4">
|
||||
@csrf
|
||||
<button type="submit" class="inline-flex items-center px-6 py-2.5 text-sm font-semibold text-white transition rounded-lg bg-yellow-600 hover:bg-yellow-700 shadow-theme-xs">
|
||||
Run CA Setup Now
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<div class="mt-4 p-3 bg-yellow-200/50 rounded text-sm text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-200">
|
||||
<strong>Action Required:</strong> Please contact your administrator to initialize the Root CA.
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="space-y-6" x-data="{
|
||||
search: '{{ $search }}',
|
||||
perPage: '{{ $perPage }}',
|
||||
loading: false,
|
||||
updateTable(url = null) {
|
||||
this.loading = true;
|
||||
let baseUrl = url || '{{ route('certificate.index') }}';
|
||||
let finalUrl = new URL(baseUrl);
|
||||
|
||||
if (!url) {
|
||||
finalUrl.searchParams.set('search', this.search);
|
||||
finalUrl.searchParams.set('per_page', this.perPage);
|
||||
}
|
||||
|
||||
fetch(finalUrl, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(html => {
|
||||
let container = document.getElementById('certificate-table-container');
|
||||
container.innerHTML = html;
|
||||
this.loading = false;
|
||||
if (typeof Alpine !== 'undefined') {
|
||||
Alpine.initTree(container);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error fetching content:', err);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
// View Modal Logic
|
||||
viewOpen: false,
|
||||
viewTitle: '',
|
||||
viewContent: '',
|
||||
viewUrl: '',
|
||||
loading: false,
|
||||
copied: false,
|
||||
|
||||
openViewModal(url, title) {
|
||||
this.viewUrl = url;
|
||||
this.viewTitle = title;
|
||||
this.viewContent = '';
|
||||
this.viewOpen = true;
|
||||
this.loading = true;
|
||||
this.copied = false;
|
||||
|
||||
fetch(url)
|
||||
.then(res => res.text())
|
||||
.then(text => {
|
||||
this.viewContent = text;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error fetching content:', err);
|
||||
this.viewContent = 'Failed to load content.';
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
copyToClipboard() {
|
||||
navigator.clipboard.writeText(this.viewContent).then(() => {
|
||||
this.copied = true;
|
||||
setTimeout(() => this.copied = false, 2000);
|
||||
});
|
||||
}
|
||||
}">
|
||||
<x-common.component-card>
|
||||
<x-slot:header>
|
||||
<h3 class="flex items-center text-base font-medium text-gray-800 dark:text-white/90">
|
||||
<svg class="w-5 h-5 mr-2.5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path></svg>
|
||||
SSL Certificates List
|
||||
</h3>
|
||||
</x-slot:header>
|
||||
<!-- DataTables Header Utility -->
|
||||
<div class="flex flex-col gap-4 mb-6 md:flex-row md:items-center md:justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm text-gray-500">Show</span>
|
||||
<select x-model="perPage" class="px-3 py-1.5 text-sm border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700 dark:text-white focus:ring-brand-500">
|
||||
<option value="5">5</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
<span class="text-sm text-gray-500">entries</span>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full md:w-64">
|
||||
<input type="text" x-model="search" placeholder="Search certificates..."
|
||||
class="w-full pl-10 pr-4 py-2 text-sm border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700 dark:text-white focus:ring-brand-500 focus:border-brand-500">
|
||||
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
||||
<svg x-show="!loading" class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
|
||||
<svg x-show="loading" class="w-4 h-4 text-brand-500 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="certificate-table-container" class="relative">
|
||||
@include('pages.certificate.partials.table')
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
|
||||
@if($caReady)
|
||||
<x-common.component-card>
|
||||
<x-slot:header>
|
||||
<h3 class="flex items-center text-base font-medium text-gray-800 dark:text-white/90">
|
||||
<svg class="w-5 h-5 mr-2.5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
|
||||
Download Root CA Certificates
|
||||
</h3>
|
||||
</x-slot:header>
|
||||
<p class="text-sm text-gray-500 mb-6">These are the authority certificates used to sign your SSLs. Install them on your machine/browser to trust your generated certificates.</p>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<a href="{{ route('certificate.download-ca', 'root') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-brand-700 bg-brand-50 rounded-lg hover:bg-brand-100 transition dark:bg-brand-900/20 dark:text-brand-300">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a2 2 0 002 2h12a2 2 0 002-2v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
|
||||
Root CA (.crt)
|
||||
</a>
|
||||
<a href="{{ route('certificate.download-ca', 'int_2048') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition dark:bg-gray-700 dark:text-gray-300">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a2 2 0 002 2h12a2 2 0 002-2v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
|
||||
Int-2048 CA (.crt)
|
||||
</a>
|
||||
<a href="{{ route('certificate.download-ca', 'int_4096') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition dark:bg-gray-700 dark:text-gray-300">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a2 2 0 002 2h12a2 2 0 002-2v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
|
||||
Int-4096 CA (.crt)
|
||||
</a>
|
||||
<a href="{{ route('certificate.download-ca-bundle') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-success-700 bg-success-50 rounded-lg hover:bg-success-100 transition dark:bg-success-900/20 dark:text-success-300">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
|
||||
CA Bundle (Windows)
|
||||
</a>
|
||||
<a href="{{ route('certificate.download-ca-android') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-warning-700 bg-warning-50 rounded-lg hover:bg-warning-100 transition dark:bg-warning-900/20 dark:text-warning-300">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"></path></svg>
|
||||
CA Android (.der)
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700">
|
||||
<a href="{{ route('certificate.download-installer') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition rounded-lg bg-brand-500 hover:bg-brand-600 shadow-theme-xs">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a2 2 0 002 2h12a2 2 0 002-2v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
|
||||
Download Windows One-Click Installer (.bat)
|
||||
</a>
|
||||
</div>
|
||||
</x-common.component-card>
|
||||
@endif
|
||||
|
||||
@include('pages.certificate.partials.view-modal')
|
||||
</div>
|
||||
|
||||
@include('pages.certificate.partials.create-modal')
|
||||
@endsection
|
||||
@@ -0,0 +1,137 @@
|
||||
<x-ui.modal x-model="createOpen" containerClass="max-w-3xl" style="z-index: 100009;">
|
||||
<div class="p-6 sm:p-10">
|
||||
<div class="mb-6 pr-10">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Generate SSL Certificate</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 text-balance">Create a new self-signed certificate using your Local CA. This will be signed by your private root authority.</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('certificate.generate') }}" method="POST" class="space-y-8"
|
||||
x-data="{
|
||||
common_name: '{{ old('common_name') }}',
|
||||
config_mode: '{{ old('config_mode', 'default') }}',
|
||||
key_bits: '2048',
|
||||
san: '{{ old('san') }}',
|
||||
isValid() {
|
||||
return this.common_name.length > 3 && this.common_name.includes('.');
|
||||
}
|
||||
}">
|
||||
@csrf
|
||||
|
||||
<!-- Configuration Mode -->
|
||||
<div>
|
||||
<label class="block mb-3 text-sm font-medium text-gray-700 dark:text-gray-300">Configuration Mode</label>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<label class="relative flex p-4 cursor-pointer rounded-xl border transition-all"
|
||||
:class="config_mode === 'default' ? 'border-brand-500 ring-1 ring-brand-500 bg-brand-50/50 dark:bg-brand-900/10 dark:border-brand-400' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'">
|
||||
<input type="radio" name="config_mode" value="default" x-model="config_mode" class="sr-only">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center justify-center flex-shrink-0 w-5 h-5 mt-0.5 border rounded-full transition-colors"
|
||||
:class="config_mode === 'default' ? 'border-brand-500 bg-brand-500' : 'border-gray-300 dark:border-gray-600'">
|
||||
<div class="w-2 h-2 bg-white rounded-full" x-show="config_mode === 'default'"></div>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-medium text-gray-900 dark:text-white">Default Presets</span>
|
||||
<span class="block mt-0.5 text-xs text-gray-500 dark:text-gray-400">Use system defaults for Organization, Locality, and Country settings.</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="relative flex p-4 cursor-pointer rounded-xl border transition-all"
|
||||
:class="config_mode === 'manual' ? 'border-brand-500 ring-1 ring-brand-500 bg-brand-50/50 dark:bg-brand-900/10 dark:border-brand-400' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'">
|
||||
<input type="radio" name="config_mode" value="manual" x-model="config_mode" class="sr-only">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center justify-center flex-shrink-0 w-5 h-5 mt-0.5 border rounded-full transition-colors"
|
||||
:class="config_mode === 'manual' ? 'border-brand-500 bg-brand-500' : 'border-gray-300 dark:border-gray-600'">
|
||||
<div class="w-2 h-2 bg-white rounded-full" x-show="config_mode === 'manual'"></div>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-medium text-gray-900 dark:text-white">Manual Configuration</span>
|
||||
<span class="block mt-0.5 text-xs text-gray-500 dark:text-gray-400">Customise all Distinguised Name (DN) attributes manually.</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Details -->
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Common Name (Domain) <span class="text-error-500">*</span></label>
|
||||
<input type="text" name="common_name" x-model="common_name" placeholder="e.g. example.com or e.g. 127.0.0.1" required
|
||||
class="w-full px-4 py-2.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white transition-colors"
|
||||
:class="common_name && !isValid() ? 'border-error-500 focus:ring-error-500/20 focus:border-error-500' : ''">
|
||||
<p x-show="common_name && !isValid()" class="mt-1.5 text-xs text-error-500 animate-pulse">Please enter a valid domain name containing at least one dot.</p>
|
||||
<p class="mt-1.5 text-xs text-gray-500">The primary Fully Qualified Domain Name (FQDN) or IP Address to be secured.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Subject Alternative Names (SAN)</label>
|
||||
<input type="text" name="san" x-model="san" placeholder="e.g. api.local, 192.168.1.50"
|
||||
class="w-full px-4 py-2.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white transition-colors">
|
||||
<p class="mt-1.5 text-xs text-gray-500">Optional comma-separated list of additional domains or IPs.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manual Mode Fields -->
|
||||
<div x-show="config_mode === 'manual'" x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2" x-transition:enter-end="opacity-100 translate-y-0"
|
||||
class="p-5 border border-gray-100 rounded-xl bg-gray-50 dark:bg-gray-800/50 dark:border-gray-700/50">
|
||||
|
||||
<h4 class="mb-4 text-xs font-semibold tracking-wider text-gray-500 uppercase font-lexend">Distinguished Name Attributes</h4>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">Organization (O)</label>
|
||||
<input type="text" name="organization" value="{{ $defaults['organizationName'] ?? '' }}"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">Country (C)</label>
|
||||
<input type="text" name="country" value="{{ $defaults['countryName'] ?? 'ID' }}" maxlength="2"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">State / Province (ST)</label>
|
||||
<input type="text" name="state" value="{{ $defaults['stateOrProvinceName'] ?? '' }}"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-1.5 text-sm font-medium text-gray-700 dark:text-gray-300">Locality (L)</label>
|
||||
<input type="text" name="locality" value="{{ $defaults['localityName'] ?? '' }}"
|
||||
class="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-brand-500 focus:border-brand-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Key Size -->
|
||||
<div>
|
||||
<label class="block mb-3 text-sm font-medium text-gray-700 dark:text-gray-300">Private Key Size</label>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<label class="cursor-pointer">
|
||||
<input type="radio" name="key_bits" value="2048" x-model="key_bits" class="peer sr-only">
|
||||
<div class="px-4 py-2 text-sm font-medium text-gray-600 bg-white border border-gray-200 rounded-lg peer-checked:bg-brand-50 peer-checked:text-brand-700 peer-checked:border-brand-500 hover:bg-gray-50 transition-all dark:bg-gray-800 dark:border-gray-700 dark:text-gray-300 dark:peer-checked:bg-brand-900/30 dark:peer-checked:text-brand-300 dark:peer-checked:border-brand-500">
|
||||
2048 Bit (Standard)
|
||||
</div>
|
||||
</label>
|
||||
<label class="cursor-pointer">
|
||||
<input type="radio" name="key_bits" value="4096" x-model="key_bits" class="peer sr-only">
|
||||
<div class="px-4 py-2 text-sm font-medium text-gray-600 bg-white border border-gray-200 rounded-lg peer-checked:bg-brand-50 peer-checked:text-brand-700 peer-checked:border-brand-500 hover:bg-gray-50 transition-all dark:bg-gray-800 dark:border-gray-700 dark:text-gray-300 dark:peer-checked:bg-brand-900/30 dark:peer-checked:text-brand-300 dark:peer-checked:border-brand-500">
|
||||
4096 Bit (High Security)
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center justify-end gap-3 pt-4 sm:pt-6 border-t border-gray-100 dark:border-gray-800">
|
||||
<button type="button" @click="createOpen = false" class="px-5 py-2.5 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700 transition shadow-sm">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" :disabled="!isValid()"
|
||||
class="px-6 py-2.5 text-sm font-semibold text-white transition rounded-lg bg-brand-500 hover:bg-brand-600 shadow-theme-xs disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
Generate Certificate
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
172
resources/views/pages/certificate/partials/table.blade.php
Normal file
172
resources/views/pages/certificate/partials/table.blade.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<div class="max-w-full overflow-x-auto"
|
||||
x-data="{ anyOpen: false }"
|
||||
:class="anyOpen ? 'pb-32' : 'pb-4'">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-100 dark:border-gray-800">
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">No</th>
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Common Name (CN)</th>
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider">Serial Number</th>
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider hidden lg:table-cell">Alt Names (SAN)</th>
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider text-center">Status</th>
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider text-center hidden xl:table-cell">Validity Period</th>
|
||||
<th class="px-5 py-4 text-xs font-bold text-gray-500 uppercase tracking-wider text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
|
||||
@forelse ($certificates as $cert)
|
||||
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/50 transition-colors">
|
||||
<td class="px-5 py-4 text-sm text-gray-500">
|
||||
{{ ($certificates->currentPage() - 1) * $certificates->perPage() + $loop->iteration }}
|
||||
</td>
|
||||
<td class="px-5 py-4">
|
||||
<div class="text-sm font-semibold text-gray-800 dark:text-white/90">{{ $cert->common_name }}</div>
|
||||
<div class="text-xs text-gray-400">{{ $cert->organization }}</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-xs font-mono text-gray-500 dark:text-gray-400 hidden sm:table-cell">
|
||||
{{ $cert->serial_number ?: '-' }}
|
||||
</td>
|
||||
<td class="px-5 py-4 hidden lg:table-cell">
|
||||
<div class="max-w-xs truncate text-xs text-gray-500 dark:text-gray-400 font-mono" title="{{ $cert->san }}">
|
||||
{{ $cert->san ?: '-' }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-center">
|
||||
@php
|
||||
$isValid = $cert->valid_to && \Carbon\Carbon::parse($cert->valid_to)->isFuture();
|
||||
$statusClass = $isValid ? 'bg-success-50 text-success-700 dark:bg-success-900/30 dark:text-success-300' : 'bg-error-50 text-error-700 dark:bg-error-900/30 dark:text-error-300';
|
||||
$statusText = $isValid ? 'Valid' : 'Expired';
|
||||
if(!$cert->valid_to) {
|
||||
$statusClass = 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400';
|
||||
$statusText = 'Unknown';
|
||||
}
|
||||
@endphp
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 text-xs font-medium rounded-full {{ $statusClass }}">
|
||||
{{ $statusText }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-xs text-gray-500 dark:text-gray-400 text-center whitespace-nowrap hidden xl:table-cell">
|
||||
@if($cert->valid_from && $cert->valid_to)
|
||||
<div>{{ \Carbon\Carbon::parse($cert->valid_from)->format('Y-m-d') }}</div>
|
||||
<div class="text-gray-400">to</div>
|
||||
<div>{{ \Carbon\Carbon::parse($cert->valid_to)->format('Y-m-d') }}</div>
|
||||
@else
|
||||
<span class="text-gray-400">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-5 py-4">
|
||||
<div class="flex items-center justify-center gap-1.5">
|
||||
<!-- View Dropdown -->
|
||||
<div x-data="{ open: false }" class="relative" @click.away="open = false; anyOpen = false">
|
||||
<button @click="open = !open; anyOpen = open"
|
||||
class="inline-flex items-center px-2.5 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 transition-all active:scale-90 shadow-sm"
|
||||
title="View Details">
|
||||
<svg class="w-3.5 h-3.5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg>
|
||||
<span class="hidden xl:inline">View</span>
|
||||
</button>
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 translate-y-2"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
class="absolute left-0 mt-2 w-32 rounded-xl shadow-2xl bg-white dark:bg-gray-800 ring-1 ring-black/5 z-[110] overflow-hidden border border-gray-100 dark:border-gray-700">
|
||||
<button @click.prevent="openViewModal('{{ route('certificate.view', [$cert->uuid, 'cert']) }}', 'Certificate Content'); open = false" class="w-full flex items-center px-4 py-2 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
|
||||
Cert
|
||||
</button>
|
||||
<button @click.prevent="openViewModal('{{ route('certificate.view', [$cert->uuid, 'key']) }}', 'Private Key Content'); open = false" class="w-full flex items-center px-4 py-2 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"></path></svg>
|
||||
Key
|
||||
</button>
|
||||
<button @click.prevent="openViewModal('{{ route('certificate.view', [$cert->uuid, 'csr']) }}', 'CSR Content'); open = false" class="w-full flex items-center px-4 py-2 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path></svg>
|
||||
CSR
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Download Dropdown -->
|
||||
<div x-data="{ open: false }" class="relative" @click.away="open = false; anyOpen = false">
|
||||
<button @click="open = !open; anyOpen = open"
|
||||
class="inline-flex items-center px-2.5 py-1.5 text-xs font-medium text-white bg-brand-500 rounded-lg hover:bg-brand-600 transition-all active:scale-90 shadow-md"
|
||||
title="Download Files">
|
||||
<svg class="w-3.5 h-3.5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a2 2 0 002 2h12a2 2 0 002-2v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
|
||||
<span class="hidden xl:inline">Download</span>
|
||||
</button>
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 translate-y-2"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
class="absolute left-0 mt-2 w-36 rounded-xl shadow-2xl bg-white dark:bg-gray-800 ring-1 ring-black/5 z-[110] overflow-hidden border border-gray-100 dark:border-gray-700">
|
||||
<a href="{{ route('certificate.download-zip', $cert->uuid) }}" class="flex items-center px-4 py-2 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
|
||||
ZIP Bundle
|
||||
</a>
|
||||
<a href="{{ route('certificate.download-p12', $cert->uuid) }}" class="flex items-center px-4 py-2 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
|
||||
.p12 File
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Others Dropdown -->
|
||||
<div x-data="{ open: false }" class="relative" @click.away="open = false; anyOpen = false">
|
||||
<button @click="open = !open; anyOpen = open"
|
||||
class="inline-flex items-center px-2.5 py-1.5 text-xs font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 transition-all active:scale-90 shadow-sm"
|
||||
title="Other Actions">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"></path></svg>
|
||||
</button>
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 translate-y-2"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
class="absolute right-0 mt-2 w-32 rounded-xl shadow-2xl bg-white dark:bg-gray-800 ring-1 ring-black/5 z-[110] overflow-hidden border border-gray-100 dark:border-gray-700">
|
||||
<form action="{{ route('certificate.regenerate', $cert->uuid) }}" method="POST" onsubmit="return confirm('Regenerate this certificate? This will replace the current certificate and key.')">
|
||||
@csrf
|
||||
<button type="submit" class="w-full flex items-center px-4 py-2 text-xs text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg>
|
||||
Regen
|
||||
</button>
|
||||
</form>
|
||||
<form action="{{ route('certificate.delete', $cert->uuid) }}" method="POST" onsubmit="return confirm('Truly delete this certificate?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="w-full flex items-center px-4 py-2 text-xs text-error-600 hover:bg-error-50 dark:hover:bg-error-900/20 transition">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-5 py-12 text-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<svg class="w-12 h-12 text-gray-200 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
|
||||
<p class="text-sm text-gray-500 italic">No certificates found matching your criteria.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
|
||||
{{-- Filler rows to maintain professional layout height --}}
|
||||
@php
|
||||
$currentCount = $certificates->count();
|
||||
$fillCount = 5 - ($currentCount ?: 1);
|
||||
@endphp
|
||||
@for($i = 0; $i < $fillCount; $i++)
|
||||
<tr class="border-transparent pointer-events-none select-none">
|
||||
<td class="px-5 py-4" colspan="7"> </td>
|
||||
</tr>
|
||||
@endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if ($certificates->hasPages())
|
||||
<div class="px-5 py-4 border-t border-gray-100 dark:border-gray-800 ajax-pagination">
|
||||
{{ $certificates->links() }}
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,42 @@
|
||||
<x-ui.modal x-model="viewOpen" containerClass="w-full max-w-[95vw] sm:w-auto sm:min-w-[500px] sm:max-w-3xl" style="z-index: 100010;">
|
||||
<div class="flex flex-col h-[80vh] sm:h-auto sm:max-h-[85vh]">
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-5 border-b border-gray-100 dark:border-gray-800 flex items-center justify-between bg-white dark:bg-gray-900 sticky top-0 z-10 gap-8">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white" x-text="viewTitle">Certificate Content</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">View and copy the raw content below.</p>
|
||||
</div>
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center gap-2">
|
||||
<a :href="viewUrl" target="_blank"
|
||||
class="inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700 dark:hover:bg-gray-700 transition">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg>
|
||||
Raw View
|
||||
</a>
|
||||
<button @click="copyToClipboard()"
|
||||
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition rounded-lg bg-brand-500 hover:bg-brand-600 shadow-theme-xs">
|
||||
<svg x-show="!copied" class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"></path></svg>
|
||||
<svg x-show="copied" class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
||||
<span x-text="copied ? 'Copied!' : 'Copy Content'"></span>
|
||||
</button>
|
||||
<button @click="viewOpen = false" class="ml-2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300 transition-colors">
|
||||
<span class="sr-only">Close</span>
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 overflow-hidden relative bg-gray-50 dark:bg-gray-950">
|
||||
<!-- Loading State -->
|
||||
<div x-show="loading" class="absolute inset-0 flex items-center justify-center bg-white/50 dark:bg-gray-900/50 backdrop-blur-sm z-20">
|
||||
<svg class="w-8 h-8 text-brand-500 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path></svg>
|
||||
</div>
|
||||
|
||||
<!-- Editor-like View -->
|
||||
<div class="h-full overflow-auto custom-scrollbar p-0">
|
||||
<pre class="p-6 text-sm font-mono leading-relaxed text-gray-800 dark:text-gray-200 whitespace-pre-wrap break-all select-all font-fira-code" x-text="viewContent"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-ui.modal>
|
||||
22
resources/views/pages/chart/bar-chart.blade.php
Normal file
22
resources/views/pages/chart/bar-chart.blade.php
Normal file
@@ -0,0 +1,22 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="Bar chart" />
|
||||
<div class="space-y-6">
|
||||
<x-common.component-card title="Bar chart 1">
|
||||
<!-- ====== Bar Chart One Start -->
|
||||
<div class="custom-scrollbar max-w-full overflow-x-auto">
|
||||
<div id="chartOne" class="min-w-[1000px]"></div>
|
||||
</div>
|
||||
<!-- ====== Bar Chart One End -->
|
||||
</x-common.component-card>
|
||||
|
||||
<x-common.component-card title="Bar chart 2">
|
||||
<!-- ====== Bar Chart Two Start -->
|
||||
<div class="custom-scrollbar max-w-full overflow-x-auto">
|
||||
<div id="chartSix" class="min-w-[1000px]"></div>
|
||||
</div>
|
||||
<!-- ====== Bar Chart Two End -->
|
||||
</x-common.component-card>
|
||||
</div>
|
||||
@endsection
|
||||
30
resources/views/pages/chart/line-chart.blade.php
Normal file
30
resources/views/pages/chart/line-chart.blade.php
Normal file
@@ -0,0 +1,30 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="Line chart" />
|
||||
<div class="space-y-6">
|
||||
<x-common.component-card title="Line chart 1">
|
||||
<!-- ====== Line Chart One Start -->
|
||||
<div class="custom-scrollbar max-w-full overflow-x-auto">
|
||||
<div id="chartThree" class="min-w-[1000px]"></div>
|
||||
</div>
|
||||
<!-- ====== Line Chart One End -->
|
||||
</x-common.component-card>
|
||||
|
||||
<x-common.component-card title="Line chart 2">
|
||||
<!-- ====== Line Chart Two Start -->
|
||||
<div class="custom-scrollbar max-w-full overflow-x-auto">
|
||||
<div id="chartEight" class="min-w-[1000px]"></div>
|
||||
</div>
|
||||
<!-- ====== Line Chart Two End -->
|
||||
</x-common.component-card>
|
||||
|
||||
<x-common.component-card title="Line chart 3">
|
||||
<!-- ====== Line Chart Three Start -->
|
||||
<div class="custom-scrollbar max-w-full overflow-x-auto">
|
||||
<div id="chartThirteen" class="min-w-[1000px]"></div>
|
||||
</div>
|
||||
<!-- ====== Line Chart Three End -->
|
||||
</x-common.component-card>
|
||||
</div>
|
||||
@endsection
|
||||
209
resources/views/pages/dashboard.blade.php
Normal file
209
resources/views/pages/dashboard.blade.php
Normal file
@@ -0,0 +1,209 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6" x-data="{
|
||||
loading: false,
|
||||
latency: '---',
|
||||
async refreshDashboard() {
|
||||
this.loading = true;
|
||||
window.location.reload();
|
||||
},
|
||||
async measureLatency() {
|
||||
const start = performance.now();
|
||||
try {
|
||||
await fetch(window.location.origin + '/ping', { method: 'HEAD', cache: 'no-cache' });
|
||||
const end = performance.now();
|
||||
this.latency = Math.round(end - start) + 'ms';
|
||||
} catch (e) {
|
||||
this.latency = 'Offline';
|
||||
}
|
||||
}
|
||||
}" x-init="measureLatency(); setInterval(() => measureLatency(), 5000)">
|
||||
<!-- Top Header -->
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Dashboard Oversight</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Total control over your security infrastructure and API integrations.</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button @click="refreshDashboard()"
|
||||
class="p-2 text-gray-500 hover:text-brand-500 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 transition-all hover:shadow-sm"
|
||||
title="Refresh Data">
|
||||
<svg :class="loading ? 'animate-spin' : ''" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
</button>
|
||||
<a href="{{ route('api-keys.index') }}" class="px-4 py-2 bg-brand-500 hover:bg-brand-600 text-white text-sm font-medium rounded-lg transition-all shadow-lg shadow-brand-500/20">
|
||||
Manage Keys
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metric Cards -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<!-- Card 1: Total Certificates -->
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-sm relative overflow-hidden group">
|
||||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:scale-110 transition-transform">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-brand-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Certificates</span>
|
||||
<span class="text-3xl font-bold text-gray-900 dark:text-white mt-1">{{ $totalCertificates }}</span>
|
||||
<span class="text-xs text-green-500 flex items-center gap-1 mt-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M12.395 6.227a.75.75 0 011.082.022l3.992 4.497a.75.75 0 01-1.104 1.012l-3.469-3.908-4.496 3.992a.75.75 0 01-1.012-1.104l5.007-4.511z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{ $activeCertificates }} Active Now
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 2: API Keys -->
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-sm relative overflow-hidden group">
|
||||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:scale-110 transition-transform">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11.536 11 9 13.536 7.464 12 4.929 14.536V17h2.472l4.243-4.243a6 6 0 018.828-5.743zM16.5 13.5V18h6v-4.5h-6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Manageable API Keys</span>
|
||||
<span class="text-3xl font-bold text-gray-900 dark:text-white mt-1">{{ $totalApiKeys }}</span>
|
||||
<span class="text-xs text-blue-500 flex items-center gap-1 mt-2">
|
||||
Latest usage: {{ $recentApiActivity->first()->last_used_at?->diffForHumans() ?? 'None' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 3: Expiring Soon -->
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-sm relative overflow-hidden group">
|
||||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:scale-110 transition-transform">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-orange-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Expiring Soon</span>
|
||||
<span class="text-3xl font-bold text-gray-900 dark:text-white mt-1">{{ $expiringSoonCount }}</span>
|
||||
<span class="text-xs text-orange-500 flex items-center gap-1 mt-2">
|
||||
Action required within 14 days
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 4: System Health -->
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-sm relative overflow-hidden group">
|
||||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:scale-110 transition-transform">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Node Status</span>
|
||||
<span class="text-3xl font-bold mt-1" :class="latency === 'Offline' ? 'text-red-500' : 'text-green-500'" x-text="latency === 'Offline' ? 'Offline' : 'Operational'">Operational</span>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mt-2">
|
||||
Latency: <span x-text="latency"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||
<!-- Analytics Chart -->
|
||||
<div class="lg:col-span-8 bg-white dark:bg-gray-800 rounded-2xl border border-gray-200 dark:border-gray-700 p-6 flex flex-col">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h3 class="font-bold text-gray-900 dark:text-white">Certificate Issuance Trends</h3>
|
||||
<span class="text-xs font-medium text-gray-400">Last 6 Months</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex items-end justify-between gap-2 min-h-[200px] px-2">
|
||||
@foreach($issuanceData as $index => $count)
|
||||
<div class="flex-1 flex flex-col items-center gap-2 group">
|
||||
<div class="relative w-full flex items-end justify-center">
|
||||
@php
|
||||
$max = max($issuanceData) ?: 1;
|
||||
$percentage = ($count / $max) * 100;
|
||||
@endphp
|
||||
<div class="w-full max-w-[40px] bg-brand-500/20 dark:bg-brand-500/10 rounded-t-lg group-hover:bg-brand-500/30 transition-all cursor-pointer relative"
|
||||
style="height: {{ max($percentage, 5) }}%;">
|
||||
<div class="absolute -top-8 left-1/2 -translate-x-1/2 bg-gray-900 dark:bg-gray-700 text-white text-[10px] py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
{{ $count }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-[10px] font-bold text-gray-400 uppercase">{{ $months[$index] }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent API Activity -->
|
||||
<div class="lg:col-span-4 bg-white dark:bg-gray-800 rounded-2xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h3 class="font-bold text-gray-900 dark:text-white mb-6">Recent API Activity</h3>
|
||||
<div class="space-y-4">
|
||||
@forelse($recentApiActivity as $activity)
|
||||
<div class="flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-900/50 rounded-xl border border-gray-100 dark:border-gray-700">
|
||||
<div class="p-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg text-blue-600 dark:text-blue-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11.536 11 9 13.536 7.464 12 4.929 14.536V17h2.472l4.243-4.243a6 6 0 018.828-5.743zM16.5 13.5V18h6v-4.5h-6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-semibold text-gray-900 dark:text-white truncate">{{ $activity->name }}</p>
|
||||
<p class="text-[10px] text-gray-500 dark:text-gray-400 mt-0.5">Used {{ $activity->last_used_at->diffForHumans() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-center py-6">
|
||||
<p class="text-sm text-gray-400">No recent activity detected.</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Latest Certificates -->
|
||||
<div class="lg:col-span-12 bg-white dark:bg-gray-800 rounded-2xl border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<div class="p-6 border-b border-gray-100 dark:border-gray-700 flex items-center justify-between">
|
||||
<h3 class="font-bold text-gray-900 dark:text-white">Recently Issued Certificates</h3>
|
||||
<a href="#" class="text-xs font-bold text-brand-500 hover:text-brand-600 uppercase tracking-wider">View All</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left">
|
||||
<thead>
|
||||
<tr class="text-[10px] font-bold text-gray-400 uppercase tracking-widest bg-gray-50 dark:bg-gray-900/50">
|
||||
<th class="px-6 py-4">Common Name</th>
|
||||
<th class="px-6 py-4">Organization</th>
|
||||
<th class="px-6 py-4">Issued At</th>
|
||||
<th class="px-6 py-4">Expires</th>
|
||||
<th class="px-6 py-4 text-right">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
@forelse($recentCertificates as $cert)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900 dark:text-white">{{ $cert->common_name }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">{{ $cert->organization }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">{{ $cert->created_at->format('M d, Y') }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">{{ $cert->valid_to->format('M d, Y') }}</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
@if($cert->valid_to > now())
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-500 uppercase">Valid</span>
|
||||
@else
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-500 uppercase">Expired</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-8 text-center text-sm text-gray-500 dark:text-gray-400">No certificates found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
33
resources/views/pages/errors/error-404.blade.php
Normal file
33
resources/views/pages/errors/error-404.blade.php
Normal file
@@ -0,0 +1,33 @@
|
||||
@extends('layouts.fullscreen-layout')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$currentYear = date('Y');
|
||||
@endphp
|
||||
<div class="relative flex flex-col items-center justify-center min-h-screen p-6 overflow-hidden z-1">
|
||||
{{-- common grid shape --}}
|
||||
<x-common.common-grid-shape />
|
||||
<!-- Centered Content -->
|
||||
<div class="mx-auto w-full max-w-[242px] text-center sm:max-w-[472px]">
|
||||
<h1 class="mb-8 font-bold text-gray-800 text-title-md dark:text-white/90 xl:text-title-2xl">
|
||||
ERROR
|
||||
</h1>
|
||||
|
||||
<img src="/images/error/404.svg" alt="404" class="dark:hidden" />
|
||||
<img src="/images/error/404-dark.svg" alt="404" class="hidden dark:block" />
|
||||
|
||||
<p class="mt-10 mb-6 text-base text-gray-700 dark:text-gray-400 sm:text-lg">
|
||||
We can't seem to find the page you are looking for!
|
||||
</p>
|
||||
|
||||
<a href="/"
|
||||
class="inline-flex items-center justify-center rounded-lg border border-gray-300 bg-white px-5 py-3.5 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
||||
Back to Home Page
|
||||
</a>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<p class="absolute text-sm text-center text-gray-500 -translate-x-1/2 bottom-6 left-1/2 dark:text-gray-400">
|
||||
© {{ $currentYear }} - {{ config('app.name') }}
|
||||
</p>
|
||||
</div>
|
||||
@endsection
|
||||
21
resources/views/pages/form/form-elements.blade.php
Normal file
21
resources/views/pages/form/form-elements.blade.php
Normal file
@@ -0,0 +1,21 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="From Elements" />
|
||||
<div class="grid grid-cols-1 gap-6 xl:grid-cols-2">
|
||||
<div class="space-y-6">
|
||||
<x-form.form-elements.default-inputs />
|
||||
<x-form.form-elements.select-inputs />
|
||||
<x-form.form-elements.text-area-inputs />
|
||||
<x-form.form-elements.input-states />
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
<x-form.form-elements.input-group />
|
||||
<x-form.form-elements.file-input-example />
|
||||
<x-form.form-elements.checkbox-component />
|
||||
<x-form.form-elements.radio-buttons />
|
||||
<x-form.form-elements.toggle-switch />
|
||||
<x-form.form-elements.dropzone />
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
11
resources/views/pages/profile.blade.php
Normal file
11
resources/views/pages/profile.blade.php
Normal file
@@ -0,0 +1,11 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<x-common.page-breadcrumb pageTitle="User Profile" />
|
||||
<div class="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] lg:p-6">
|
||||
<h3 class="mb-5 text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-7">Profile</h3>
|
||||
<x-profile.profile-card :user="$user" />
|
||||
<x-profile.personal-info-card :user="$user" />
|
||||
<x-profile.address-card :user="$user" />
|
||||
</div>
|
||||
@endsection
|
||||
517
resources/views/pages/settings.blade.php
Normal file
517
resources/views/pages/settings.blade.php
Normal file
@@ -0,0 +1,517 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="mx-auto max-w-(--breakpoint-2xl) p-4 md:p-6" x-data="{
|
||||
showProfileModal: false,
|
||||
showAvatarModal: false,
|
||||
showPasswordModal: false,
|
||||
showSocialModal: false,
|
||||
showDeleteModal: false,
|
||||
socialProvider: '',
|
||||
socialProviderName: '',
|
||||
socialAction: 'connect',
|
||||
socialForm: null,
|
||||
socialConnectUrl: '',
|
||||
deleteConfirmation: '',
|
||||
activeSection: '#profile',
|
||||
mobileNavOpen: false,
|
||||
init() {
|
||||
// Smooth scrolling behavior and active section tracking
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
this.activeSection = '#' + entry.target.id;
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.3 });
|
||||
|
||||
['profile', 'security', 'social', 'access', 'danger'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) observer.observe(el);
|
||||
});
|
||||
|
||||
// Check initial hash
|
||||
if (window.location.hash) {
|
||||
this.activeSection = window.location.hash;
|
||||
}
|
||||
},
|
||||
submitPasswordForm() {
|
||||
this.$refs.passwordForm.submit();
|
||||
},
|
||||
confirmSocialDisconnect(providerName, providerSlug, form) {
|
||||
this.socialProviderName = providerName;
|
||||
this.socialProvider = providerSlug;
|
||||
this.socialAction = 'disconnect';
|
||||
this.socialForm = form;
|
||||
this.showSocialModal = true;
|
||||
},
|
||||
confirmSocialConnect(providerName, url) {
|
||||
// Direct redirect without modal for connect
|
||||
window.location.href = url;
|
||||
},
|
||||
submitSocialForm() {
|
||||
if (this.socialAction === 'connect') {
|
||||
window.location.href = this.socialConnectUrl;
|
||||
} else {
|
||||
this.socialForm.submit();
|
||||
}
|
||||
},
|
||||
saveProfile() {
|
||||
this.$refs.profileForm.submit();
|
||||
}
|
||||
}">
|
||||
<style>
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
[id] {
|
||||
scroll-margin-top: 100px;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
[id] {
|
||||
scroll-margin-top: 140px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Breadcrumb -->
|
||||
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white/90">
|
||||
Account Settings
|
||||
</h2>
|
||||
|
||||
<nav>
|
||||
<ol class="flex items-center gap-2">
|
||||
<li>
|
||||
<a class="font-medium text-gray-500 hover:text-brand-500 dark:text-gray-400 dark:hover:text-brand-500"
|
||||
href="{{ route('dashboard') }}">
|
||||
Dashboard /
|
||||
</a>
|
||||
</li>
|
||||
<li class="font-medium text-brand-500">Settings</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||
<!-- Sidebar-like navigation for settings -->
|
||||
<div class="lg:col-span-1">
|
||||
<div class="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900 sticky top-24 z-10">
|
||||
<div class="p-4 sm:p-6">
|
||||
<!-- Mobile Header -->
|
||||
<div class="lg:hidden flex items-center justify-between mb-2">
|
||||
<span class="text-[10px] font-bold uppercase tracking-widest text-gray-400 dark:text-gray-500">Settings Section</span>
|
||||
<button @click="mobileNavOpen = !mobileNavOpen" class="flex items-center gap-1.5 text-xs font-semibold text-brand-500 hover:text-brand-600 transition-colors">
|
||||
<span x-text="mobileNavOpen ? 'Collapse Menu' : 'Change Section'"></span>
|
||||
<svg class="transition-transform duration-200" :class="mobileNavOpen ? 'rotate-180' : ''" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="flex flex-col gap-1 transition-all duration-300">
|
||||
<!-- Profile Information -->
|
||||
<li x-show="activeSection === '#profile' || mobileNavOpen || window.innerWidth >= 1024"
|
||||
x-collapse.duration.300ms>
|
||||
<a href="#profile"
|
||||
@click="if(window.innerWidth < 1024) mobileNavOpen = false"
|
||||
class="flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||
:class="activeSection === '#profile'
|
||||
? 'bg-brand-50 text-brand-500 dark:bg-brand-500/10 dark:text-brand-500 shadow-sm'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-white/90'">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
||||
Profile Information
|
||||
<template x-if="activeSection === '#profile' && !mobileNavOpen && window.innerWidth < 1024">
|
||||
<div class="ml-auto animate-pulse h-1.5 w-1.5 rounded-full bg-brand-500"></div>
|
||||
</template>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Security & Password -->
|
||||
<li x-show="activeSection === '#security' || mobileNavOpen || window.innerWidth >= 1024"
|
||||
x-collapse.duration.300ms>
|
||||
<a href="#security"
|
||||
@click="if(window.innerWidth < 1024) mobileNavOpen = false"
|
||||
class="flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||
:class="activeSection === '#security'
|
||||
? 'bg-brand-50 text-brand-500 dark:bg-brand-500/10 dark:text-brand-500 shadow-sm'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-white/90'">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
||||
Security & Password
|
||||
<template x-if="activeSection === '#security' && !mobileNavOpen && window.innerWidth < 1024">
|
||||
<div class="ml-auto animate-pulse h-1.5 w-1.5 rounded-full bg-brand-500"></div>
|
||||
</template>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Connected Accounts -->
|
||||
<li x-show="activeSection === '#social' || mobileNavOpen || window.innerWidth >= 1024"
|
||||
x-collapse.duration.300ms>
|
||||
<a href="#social"
|
||||
@click="if(window.innerWidth < 1024) mobileNavOpen = false"
|
||||
class="flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||
:class="activeSection === '#social'
|
||||
? 'bg-brand-50 text-brand-500 dark:bg-brand-500/10 dark:text-brand-500 shadow-sm'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-white/90'">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><polyline points="17 11 19 13 23 9"></polyline></svg>
|
||||
Connected Accounts
|
||||
<template x-if="activeSection === '#social' && !mobileNavOpen && window.innerWidth < 1024">
|
||||
<div class="ml-auto animate-pulse h-1.5 w-1.5 rounded-full bg-brand-500"></div>
|
||||
</template>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Account Access -->
|
||||
<li x-show="activeSection === '#access' || mobileNavOpen || window.innerWidth >= 1024"
|
||||
x-collapse.duration.300ms>
|
||||
<a href="#access"
|
||||
@click="if(window.innerWidth < 1024) mobileNavOpen = false"
|
||||
class="flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||
:class="activeSection === '#access'
|
||||
? 'bg-brand-50 text-brand-500 dark:bg-brand-500/10 dark:text-brand-500 shadow-sm'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-white/90 dark:hover:bg-white/[0.03]'">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
||||
Account Access
|
||||
<template x-if="activeSection === '#access' && !mobileNavOpen && window.innerWidth < 1024">
|
||||
<div class="ml-auto animate-pulse h-1.5 w-1.5 rounded-full bg-brand-500"></div>
|
||||
</template>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<div class="my-2 h-px bg-gray-100 dark:bg-gray-800" x-show="mobileNavOpen || window.innerWidth >= 1024"></div>
|
||||
|
||||
<!-- Danger Zone -->
|
||||
<li x-show="activeSection === '#danger' || mobileNavOpen || window.innerWidth >= 1024"
|
||||
x-collapse.duration.300ms>
|
||||
<a href="#danger"
|
||||
@click="if(window.innerWidth < 1024) mobileNavOpen = false"
|
||||
class="flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200"
|
||||
:class="activeSection === '#danger'
|
||||
? 'bg-error-50 text-error-600 dark:bg-error-500/10 dark:text-error-400 shadow-sm'
|
||||
: 'text-error-500 hover:bg-error-50 dark:hover:bg-error-500/10'">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"></path><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
||||
Danger Zone
|
||||
<template x-if="activeSection === '#danger' && !mobileNavOpen && window.innerWidth < 1024">
|
||||
<div class="ml-auto animate-pulse h-1.5 w-1.5 rounded-full bg-error-500"></div>
|
||||
</template>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<!-- Status Messages -->
|
||||
<!-- Status Messages handled globally -->
|
||||
|
||||
<!-- Profile Information Card -->
|
||||
<div id="profile" class="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="border-b border-gray-200 p-4 sm:px-6 dark:border-gray-800 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Profile Information
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Update your personal details and avatar.
|
||||
</p>
|
||||
</div>
|
||||
<button @click="showProfileModal = true" class="text-sm font-medium text-brand-500 hover:text-brand-600">
|
||||
Edit Profile
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-6 relative">
|
||||
<div class="flex flex-col sm:flex-row gap-6">
|
||||
<!-- Avatar Section -->
|
||||
<div class="flex flex-col items-center gap-3">
|
||||
<div class="group relative h-24 w-24 overflow-hidden rounded-full border border-gray-200 dark:border-gray-800">
|
||||
<img src="{{ $user->avatar ? asset('storage/' . $user->avatar) : asset('images/user/owner.jpg') }}" alt="user" class="h-full w-full object-cover" />
|
||||
<button @click="showAvatarModal = true" class="absolute inset-0 flex items-center justify-center bg-black/40 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<svg class="text-white" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z" fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button @click="showAvatarModal = true" class="text-xs font-medium text-gray-500 hover:text-brand-500">Change Photo</button>
|
||||
</div>
|
||||
|
||||
<!-- Info Grid -->
|
||||
<div class="grid grow grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-6">
|
||||
<div>
|
||||
<p class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">Full Name</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->name }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">Email Address</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $user->email }}
|
||||
@if($user->hasVerifiedEmail())
|
||||
<span class="inline-flex items-center ml-2 px-2 py-0.5 rounded text-xs font-medium bg-green-50 text-green-700 dark:bg-green-500/15 dark:text-green-500">
|
||||
Verified
|
||||
</span>
|
||||
@else
|
||||
<span class="inline-flex items-center ml-2 px-2 py-0.5 rounded text-xs font-medium bg-red-50 text-red-700 dark:bg-red-500/15 dark:text-red-500">
|
||||
Unverified
|
||||
</span>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">Phone Number</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">{{ $user->phone ?? 'Not set' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">Bio</p>
|
||||
<p class="text-sm line-clamp-1 font-medium text-gray-800 dark:text-white/90">{{ $user->bio ?? 'Not set' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change Password Card -->
|
||||
<div id="security" class="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="border-b border-gray-200 p-4 sm:px-6 dark:border-gray-800">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Change Password
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Update your password to keep your account secure.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-6">
|
||||
<form x-ref="passwordForm" action="{{ route('settings.password') }}" method="POST" class="space-y-5" @submit.prevent="showPasswordModal = true">
|
||||
@csrf
|
||||
<!-- Old Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Current Password
|
||||
</label>
|
||||
<div x-data="{ show: false }" class="relative">
|
||||
<input :type="show ? 'text' : 'password'" name="current_password" required
|
||||
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-white/[0.03] dark:text-white/90">
|
||||
<button type="button" @click="show = !show" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<svg x-show="!show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
||||
<svg x-show="show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
@error('current_password')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- New Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
New Password
|
||||
</label>
|
||||
<div x-data="{ show: false }" class="relative">
|
||||
<input :type="show ? 'text' : 'password'" name="password" required
|
||||
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-white/[0.03] dark:text-white/90">
|
||||
<button type="button" @click="show = !show" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<svg x-show="!show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
||||
<svg x-show="show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
@error('password')
|
||||
<p class="mt-1 text-xs text-error-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div>
|
||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
||||
Confirm New Password
|
||||
</label>
|
||||
<div x-data="{ show: false }" class="relative">
|
||||
<input :type="show ? 'text' : 'password'" name="password_confirmation" required
|
||||
class="h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2 text-sm text-gray-800 placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-white/[0.03] dark:text-white/90">
|
||||
<button type="button" @click="show = !show" class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<svg x-show="!show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
||||
<svg x-show="show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3">
|
||||
<button type="submit"
|
||||
class="rounded-lg bg-brand-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-brand-600 focus:outline-hidden focus:ring-2 focus:ring-brand-500/50">
|
||||
Update Password
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Connected Accounts Card -->
|
||||
<div id="social" class="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="border-b border-gray-200 p-4 sm:px-6 dark:border-gray-800">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Connected Accounts
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Manage your linked social accounts.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-6 space-y-6">
|
||||
<!-- Google -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex h-11 w-11 items-center justify-center rounded-full border border-gray-200 dark:border-gray-800">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18.7511 10.1944C18.7511 9.47495 18.6915 8.94995 18.5626 8.40552H10.1797V11.6527H15.1003C15.0011 12.4597 14.4654 13.675 13.2749 14.4916L13.2582 14.6003L15.9087 16.6126L16.0924 16.6305C17.7788 15.1041 18.7511 12.8583 18.7511 10.1944Z" fill="#4285F4" /><path d="M10.1788 18.75C12.5895 18.75 14.6133 17.9722 16.0915 16.6305L13.274 14.4916C12.5201 15.0068 11.5081 15.3666 10.1788 15.3666C7.81773 15.3666 5.81379 13.8402 5.09944 11.7305L4.99473 11.7392L2.23868 13.8295L2.20264 13.9277C3.67087 16.786 6.68674 18.75 10.1788 18.75Z" fill="#34A853" /><path d="M5.10014 11.7305C4.91165 11.186 4.80257 10.6027 4.80257 9.99992C4.80257 9.3971 4.91165 8.81379 5.09022 8.26935L5.08523 8.1534L2.29464 6.02954L2.20333 6.0721C1.5982 7.25823 1.25098 8.5902 1.25098 9.99992C1.25098 11.4096 1.5982 12.7415 2.20333 13.9277L5.10014 11.7305Z" fill="#FBBC05" /><path d="M10.1789 4.63331C11.8554 4.63331 12.9864 5.34303 13.6312 5.93612L16.1511 3.525C14.6035 2.11528 12.5895 1.25 10.1789 1.25C6.68676 1.25 3.67088 3.21387 2.20264 6.07218L5.08953 8.26943C5.81381 6.15972 7.81776 4.63331 10.1789 4.63331Z" fill="#EB4335" /></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-gray-800 dark:text-white/90">Google</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $user->google_id ? 'Connected' : 'Not connected' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@if ($user->google_id)
|
||||
<form action="{{ route('settings.social.disconnect', 'google') }}" method="POST" x-ref="disconnectGoogleForm">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button" @click="confirmSocialDisconnect('Google', 'google', $refs.disconnectGoogleForm)" class="text-sm font-medium text-error-500 hover:text-error-600">
|
||||
Disconnect
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<a href="{{ route('auth.social', ['provider' => 'google', 'context' => 'connect']) }}" class="text-sm font-medium text-brand-500 hover:text-brand-600">
|
||||
Connect
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- GitHub -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex h-11 w-11 items-center justify-center rounded-full border border-gray-200 dark:border-gray-800 text-gray-700 dark:text-white/90">
|
||||
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-gray-800 dark:text-white/90">GitHub</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $user->github_id ? 'Connected' : 'Not connected' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@if ($user->github_id)
|
||||
<form action="{{ route('settings.social.disconnect', 'github') }}" method="POST" x-ref="disconnectGitHubForm">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button" @click="confirmSocialDisconnect('GitHub', 'github', $refs.disconnectGitHubForm)" class="text-sm font-medium text-error-500 hover:text-error-600">
|
||||
Disconnect
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<a href="{{ route('auth.social', ['provider' => 'github', 'context' => 'connect']) }}" class="text-sm font-medium text-brand-500 hover:text-brand-600">
|
||||
Connect
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account Access Card -->
|
||||
<div id="access" class="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="border-b border-gray-200 p-4 sm:px-6 dark:border-gray-800">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||
Account Access
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Recent login activity on your account (Max 10 records, last 1 month).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-6">
|
||||
@forelse($user->loginHistories as $history)
|
||||
<div class="flex items-center justify-between py-3 border-b border-gray-100 dark:border-gray-800 last:border-0">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-100 dark:bg-white/[0.03] text-gray-700 dark:text-white/90">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
@if($history->provider === 'google')
|
||||
<path d="M18.7511 10.1944C18.7511 9.47495 18.6915 8.94995 18.5626 8.40552H10.1797V11.6527H15.1003C15.0011 12.4597 14.4654 13.675 13.2749 14.4916L13.2582 14.6003L15.9087 16.6126L16.0924 16.6305C17.7788 15.1041 18.7511 12.8583 18.7511 10.1944Z" fill="#4285F4" /><path d="M10.1788 18.75C12.5895 18.75 14.6133 17.9722 16.0915 16.6305L13.274 14.4916C12.5201 15.0068 11.5081 15.3666 10.1788 15.3666C7.81773 15.3666 5.81379 13.8402 5.09944 11.7305L4.99473 11.7392L2.23868 13.8295L2.20264 13.9277C3.67087 16.786 6.68674 18.75 10.1788 18.75Z" fill="#34A853" /><path d="M5.10014 11.7305C4.91165 11.186 4.80257 10.6027 4.80257 9.99992C4.80257 9.3971 4.91165 8.81379 5.09022 8.26935L5.08523 8.1534L2.29464 6.02954L2.20333 6.0721C1.5982 7.25823 1.25098 8.5902 1.25098 9.99992C1.25098 11.4096 1.5982 12.7415 2.20333 13.9277L5.10014 11.7305Z" fill="#FBBC05" /><path d="M10.1789 4.63331C11.8554 4.63331 12.9864 5.34303 13.6312 5.93612L16.1511 3.525C14.6035 2.11528 12.5895 1.25 10.1789 1.25C6.68676 1.25 3.67088 3.21387 2.20264 6.07218L5.08953 8.26943C5.81381 6.15972 7.81776 4.63331 10.1789 4.63331Z" fill="#EB4335" />
|
||||
@elseif($history->provider === 'github')
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
|
||||
@else
|
||||
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h4M10 17l5-5-5-5M13.8 12H3"></path>
|
||||
@endif
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-white/90">
|
||||
{{ $history->login_at->format('d M Y, H:i') }}
|
||||
</p>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
({{ $history->login_at->diffForHumans() }})
|
||||
</span>
|
||||
</div>
|
||||
@if($history->ip_address)
|
||||
<div class="mt-1 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
<span class="font-mono bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded text-gray-600 dark:text-gray-300">
|
||||
{{ $history->ip_address }}
|
||||
</span>
|
||||
@if($history->user_agent)
|
||||
<span class="truncate max-w-[200px]" title="{{ $history->user_agent }}">
|
||||
Login on {{ Str::limit($history->user_agent, 40) }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize
|
||||
{{ $history->provider === 'google' ? 'bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-300' :
|
||||
($history->provider === 'github' ? 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300' :
|
||||
'bg-brand-50 text-brand-700 dark:bg-brand-900/20 dark:text-brand-300') }}">
|
||||
{{ ucfirst($history->provider) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-gray-500 italic py-4 text-center">No login history recorded yet.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Danger Zone Card -->
|
||||
<div id="danger" class="rounded-2xl border border-error-200 bg-white dark:border-error-500/30 dark:bg-gray-900">
|
||||
<div class="border-b border-error-100 p-4 sm:px-6 dark:border-error-500/20">
|
||||
<h3 class="text-lg font-semibold text-error-600 dark:text-error-400">
|
||||
Danger Zone
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Irreversible actions for your account.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 sm:p-6">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h4 class="text-sm font-medium text-gray-800 dark:text-white/90">Delete Account</h4>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
Once you delete your account, there is no going back. Please be certain.
|
||||
</p>
|
||||
</div>
|
||||
<button @click="showDeleteModal = true" class="rounded-lg bg-error-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-error-600 focus:outline-hidden focus:ring-2 focus:ring-error-500/50">
|
||||
Delete Account
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modals -->
|
||||
|
||||
<!-- Refactored Modals -->
|
||||
<x-profile.edit-modal :user="$user" show="showProfileModal" />
|
||||
<x-profile.avatar-modal :user="$user" show="showAvatarModal" />
|
||||
<x-settings.password-modal show="showPasswordModal" />
|
||||
<x-settings.social-modal show="showSocialModal" socialProvider="socialProviderName" submitAction="submitSocialForm()" />
|
||||
<x-settings.delete-account-modal show="showDeleteModal" deleteConfirmation="deleteConfirmation" />
|
||||
</div>
|
||||
@endsection
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user