├── LICENSE ├── README.md ├── composer.json ├── config └── root.php ├── database ├── factories │ ├── AuthCodeFactory.php │ ├── EventFactory.php │ ├── MediumFactory.php │ ├── MetaFactory.php │ ├── NotificationFactory.php │ ├── SettingFactory.php │ └── TranslationFactory.php ├── migrations │ ├── 2020_01_01_000100_create_root_media_table.php │ ├── 2020_07_01_000000_create_root_meta_data_table.php │ ├── 2023_08_15_101934_create_root_notifications_table.php │ ├── 2024_03_21_175513_create_root_events_table.php │ ├── 2024_05_14_091321_create_root_auth_codes_table.php │ ├── 2024_09_02_111321_create_root_settings_table.php │ └── 2024_11_07_195330_create_root_translations_table.php └── seeders │ └── RootTestDataSeeder.php ├── package.json ├── public ├── build │ ├── assets │ │ ├── app-BhI0qz-j.css │ │ ├── app-C0jciRh8.js │ │ ├── app-CAFfX1PR.js │ │ ├── chart-C0jciRh8.js │ │ ├── dropdown-C0jciRh8.js │ │ ├── editor-C0jciRh8.js │ │ ├── media-manager-C0jciRh8.js │ │ ├── repeater-C0jciRh8.js │ │ └── table-C0jciRh8.js │ └── manifest.json ├── favicon.ico ├── favicon.png ├── fonts │ ├── manrope-v14-latin-500.woff2 │ ├── manrope-v14-latin-600.woff2 │ ├── manrope-v14-latin-700.woff2 │ ├── manrope-v14-latin-800.woff2 │ ├── manrope-v14-latin-regular.woff2 │ ├── open-sans-v35-latin-700.woff2 │ └── open-sans-v35-latin-regular.woff2 ├── image-loading-placeholder.svg ├── img │ ├── auth-background.png │ ├── favicon.svg │ └── root-logo.svg └── root-logo-dark.svg ├── resources ├── js │ ├── Item.js │ ├── app.js │ ├── chart.js │ ├── dropdown.js │ ├── editor.js │ ├── helpers.js │ ├── media-manager.js │ ├── notifications.js │ ├── repeater.js │ ├── table.js │ └── theme.js ├── sass │ ├── app.scss │ ├── component │ │ ├── _alert.scss │ │ ├── _auth-form.scss │ │ ├── _block-navigation.scss │ │ ├── _breadcrumb-list.scss │ │ ├── _card.scss │ │ ├── _context-menu.scss │ │ ├── _data-group.scss │ │ ├── _data-table.scss │ │ ├── _index.scss │ │ ├── _media-uploader.scss │ │ ├── _modal.scss │ │ ├── _notification-dot.scss │ │ ├── _notification.scss │ │ ├── _open-search.scss │ │ ├── _or-separator.scss │ │ ├── _pagination.scss │ │ ├── _preloader.scss │ │ ├── _prism.scss │ │ ├── _range-group.scss │ │ ├── _search-modal.scss │ │ ├── _skip-link.scss │ │ ├── _theme-switcher.scss │ │ ├── _user-menu.scss │ │ ├── _welcome-card.scss │ │ ├── _widget.scss │ │ ├── badge │ │ │ ├── _index.scss │ │ │ ├── _status.scss │ │ │ └── _trending.scss │ │ └── form │ │ │ ├── _btn-dropdown.scss │ │ │ ├── _combobox.scss │ │ │ ├── _editor.scss │ │ │ ├── _file-group.scss │ │ │ ├── _file-list.scss │ │ │ ├── _index.scss │ │ │ ├── _repeater.scss │ │ │ └── _search-form.scss │ ├── config │ │ ├── _config.scss │ │ ├── _dark-colors.scss │ │ ├── _dark-mode.scss │ │ ├── _font.scss │ │ ├── _index.scss │ │ └── _styles.scss │ ├── extend │ │ ├── _btn.scss │ │ ├── _chart.scss │ │ ├── _form.scss │ │ ├── _index.scss │ │ └── _progress.scss │ ├── helper │ │ ├── _display.scss │ │ ├── _index.scss │ │ ├── _margin.scss │ │ └── _vertical-align.scss │ ├── layout │ │ ├── _auth.scss │ │ ├── _container.scss │ │ ├── _index.scss │ │ ├── _main.scss │ │ └── _row.scss │ └── section │ │ ├── _actions.scss │ │ ├── _body.scss │ │ ├── _footer.scss │ │ ├── _header.scss │ │ ├── _heading.scss │ │ ├── _index.scss │ │ └── _sidebar.scss └── views │ ├── actions │ ├── action.blade.php │ └── actions.blade.php │ ├── app.blade.php │ ├── auth │ ├── forgot-password.blade.php │ ├── layout.blade.php │ ├── login.blade.php │ ├── reset-password.blade.php │ └── two-factor.blade.php │ ├── components │ ├── alert.blade.php │ ├── chart.blade.php │ ├── icon.blade.php │ ├── layout │ │ ├── breadcrumbs.blade.php │ │ ├── footer.blade.php │ │ ├── header.blade.php │ │ ├── notifications.blade.php │ │ ├── sidebar-group.blade.php │ │ ├── sidebar.blade.php │ │ └── theme.blade.php │ └── modal.blade.php │ ├── dashboard.blade.php │ ├── fields │ ├── boolean.blade.php │ ├── checkbox-option.blade.php │ ├── checkbox.blade.php │ ├── date.blade.php │ ├── dropdown-option.blade.php │ ├── dropdown.blade.php │ ├── editor.blade.php │ ├── editor │ │ ├── align.blade.php │ │ ├── blocks.blade.php │ │ ├── format.blade.php │ │ ├── heading.blade.php │ │ ├── history.blade.php │ │ ├── link.blade.php │ │ ├── list.blade.php │ │ └── media.blade.php │ ├── fieldset.blade.php │ ├── file-option.blade.php │ ├── file.blade.php │ ├── hidden.blade.php │ ├── image.blade.php │ ├── input.blade.php │ ├── media.blade.php │ ├── morph-to.blade.php │ ├── panel.blade.php │ ├── range.blade.php │ ├── repeater-option.blade.php │ ├── repeater-table.blade.php │ ├── repeater.blade.php │ ├── select.blade.php │ ├── slug.blade.php │ └── textarea.blade.php │ ├── filters │ └── search.blade.php │ ├── icons │ ├── archive.blade.php │ ├── arrow-outward.blade.php │ ├── bar-chart.blade.php │ ├── bazar-emblem.blade.php │ ├── check.blade.php │ ├── chevron-down.blade.php │ ├── chevron-left.blade.php │ ├── chevron-right.blade.php │ ├── chevron-up-down.blade.php │ ├── chevron-up.blade.php │ ├── close.blade.php │ ├── cone-emblem.blade.php │ ├── dark-mode.blade.php │ ├── document.blade.php │ ├── download.blade.php │ ├── edit.blade.php │ ├── eye.blade.php │ ├── filter.blade.php │ ├── format-align-center.blade.php │ ├── format-align-justify.blade.php │ ├── format-align-left.blade.php │ ├── format-align-right.blade.php │ ├── format-bold.blade.php │ ├── format-bullet-list.blade.php │ ├── format-clear.blade.php │ ├── format-highlight.blade.php │ ├── format-italic.blade.php │ ├── format-line.blade.php │ ├── format-link.blade.php │ ├── format-ordered-list.blade.php │ ├── format-strike.blade.php │ ├── format-underline.blade.php │ ├── format-unlink.blade.php │ ├── history-do.blade.php │ ├── history-undo.blade.php │ ├── home.blade.php │ ├── image.blade.php │ ├── light-mode.blade.php │ ├── menu-open.blade.php │ ├── menu.blade.php │ ├── minus.blade.php │ ├── notifications.blade.php │ ├── pie-chart.blade.php │ ├── plus.blade.php │ ├── root-emblem.blade.php │ ├── search.blade.php │ ├── shopping-bag.blade.php │ ├── shopping-cart.blade.php │ ├── spruce-emblem.blade.php │ ├── star.blade.php │ ├── system-mode.blade.php │ ├── trash.blade.php │ ├── trending-down.blade.php │ ├── trending-up.blade.php │ └── users.blade.php │ ├── media │ ├── filters.blade.php │ ├── manager.blade.php │ ├── medium.blade.php │ └── queued-medium.blade.php │ ├── resources │ ├── form.blade.php │ ├── index.blade.php │ ├── relation.blade.php │ └── show.blade.php │ ├── table │ ├── actions.blade.php │ ├── body.blade.php │ ├── cell.blade.php │ ├── column.blade.php │ ├── filters.blade.php │ ├── pagination.blade.php │ └── table.blade.php │ └── widgets │ ├── trend.blade.php │ ├── value.blade.php │ └── welcome.blade.php ├── routes ├── api.php ├── auth.php └── web.php ├── src ├── Actions │ ├── Action.php │ ├── Actions.php │ ├── SendPasswordResetNotification.php │ └── SendVerificationNotification.php ├── Breadcrumbs │ └── Registry.php ├── Casts │ └── MetaValue.php ├── Console │ └── Commands │ │ ├── ActionMake.php │ │ ├── ClearChunks.php │ │ ├── ClearMedia.php │ │ ├── FieldMake.php │ │ ├── FilterMake.php │ │ ├── Install.php │ │ ├── Publish.php │ │ ├── ResourceMake.php │ │ ├── TrendMake.php │ │ ├── ValueMake.php │ │ └── WidgetMake.php ├── Conversion │ ├── Driver.php │ ├── GdDriver.php │ ├── Image.php │ └── Manager.php ├── Exceptions │ ├── QueryResolutionException.php │ ├── ResourceResolutionException.php │ └── SaveFormDataException.php ├── Fields │ ├── BelongsTo.php │ ├── BelongsToMany.php │ ├── Boolean.php │ ├── Checkbox.php │ ├── Color.php │ ├── Date.php │ ├── Dropdown.php │ ├── Editor.php │ ├── Email.php │ ├── Events.php │ ├── Field.php │ ├── Fields.php │ ├── Fieldset.php │ ├── File.php │ ├── HasMany.php │ ├── HasOne.php │ ├── HasOneOrMany.php │ ├── Hidden.php │ ├── ID.php │ ├── Media.php │ ├── Meta.php │ ├── MorphMany.php │ ├── MorphOne.php │ ├── MorphOneOrMany.php │ ├── MorphTo.php │ ├── MorphToMany.php │ ├── Number.php │ ├── Option.php │ ├── Radio.php │ ├── Range.php │ ├── Relation.php │ ├── Repeater.php │ ├── Select.php │ ├── Slug.php │ ├── Text.php │ ├── Textarea.php │ ├── Translations.php │ └── URL.php ├── Filters │ ├── Filter.php │ ├── Filters.php │ ├── MediaSearch.php │ ├── RenderableFilter.php │ ├── Search.php │ ├── SearchField.php │ ├── Select.php │ ├── Sort.php │ └── TrashStatus.php ├── Http │ ├── Controllers │ │ ├── ActionController.php │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── ResetPasswordController.php │ │ │ └── TwoFactorController.php │ │ ├── BelongsToManyController.php │ │ ├── Controller.php │ │ ├── DashboardController.php │ │ ├── DownloadController.php │ │ ├── MediaController.php │ │ ├── MorphToController.php │ │ ├── NotificationController.php │ │ ├── RelationController.php │ │ ├── RepeaterController.php │ │ ├── ResourceController.php │ │ └── WidgetController.php │ └── Middleware │ │ ├── Authenticate.php │ │ ├── Authorize.php │ │ └── TwoFactorAuthenticate.php ├── Interfaces │ ├── Breadcrumbs │ │ └── Registry.php │ ├── Conversion │ │ └── Manager.php │ ├── Form.php │ ├── Models │ │ ├── AuthCode.php │ │ ├── Event.php │ │ ├── Medium.php │ │ ├── Meta.php │ │ ├── Notification.php │ │ ├── Setting.php │ │ ├── Translation.php │ │ └── User.php │ ├── Navigation │ │ └── Registry.php │ └── Settings │ │ ├── Registry.php │ │ └── Repository.php ├── Jobs │ ├── MoveFile.php │ └── PerformConversions.php ├── Listeners │ └── FormatRootStubs.php ├── Models │ ├── Attachment.php │ ├── AuthCode.php │ ├── Event.php │ ├── Medium.php │ ├── Meta.php │ ├── Notification.php │ ├── Setting.php │ ├── Translation.php │ └── User.php ├── Navigation │ ├── HasItems.php │ ├── Item.php │ ├── Location.php │ └── Registry.php ├── Notifications │ ├── AuthCodeNotification.php │ ├── ResetPassword.php │ ├── RootChannel.php │ ├── RootMessage.php │ └── RootNotification.php ├── Policies │ └── MediumPolicy.php ├── Resources │ ├── Resource.php │ └── Resources.php ├── Root.php ├── RootServiceProvider.php ├── Settings │ ├── Registry.php │ └── Repository.php ├── Support │ ├── Alert.php │ ├── ClassList.php │ └── Facades │ │ └── Conversion.php ├── Traits │ ├── AsForm.php │ ├── Authorizable.php │ ├── Filterable.php │ ├── HasAttributes.php │ ├── HasMedia.php │ ├── HasMetaData.php │ ├── HasRootEvents.php │ ├── InteractsWithProxy.php │ ├── Makeable.php │ ├── RegistersRoutes.php │ ├── ResolvesActions.php │ ├── ResolvesFields.php │ ├── ResolvesFilters.php │ ├── ResolvesVisibility.php │ ├── ResolvesWidgets.php │ └── Translatable.php ├── View │ └── Components │ │ ├── Alert.php │ │ ├── Chart.php │ │ ├── Icon.php │ │ ├── Layout │ │ ├── Breadcrumbs.php │ │ ├── Footer.php │ │ ├── Header.php │ │ ├── Notifications.php │ │ ├── Sidebar.php │ │ ├── SidebarGroup.php │ │ └── Theme.php │ │ └── Modal.php └── Widgets │ ├── Metric.php │ ├── Trend.php │ ├── Value.php │ ├── Welcome.php │ ├── Widget.php │ └── Widgets.php └── stubs ├── Action.stub ├── Field.stub ├── Resource.stub ├── RootServiceProvider.stub ├── SelectFilter.stub ├── Trend.stub ├── UserResource.stub ├── Value.stub ├── Widget.stub └── placeholder.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Cone Development 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /database/factories/AuthCodeFactory.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $model = AuthCode::class; 17 | 18 | /** 19 | * Define the model's default state. 20 | */ 21 | public function definition(): array 22 | { 23 | return [ 24 | 'code' => mt_rand(100000, 999999), 25 | 'expires_at' => Date::now()->addMinutes(5), 26 | ]; 27 | } 28 | 29 | /** 30 | * Indicate that the model should be expired. 31 | */ 32 | public function expired(): static 33 | { 34 | return $this->state(fn (array $attributes) => [ 35 | 'expires_at' => Date::now()->subMinute(), 36 | ]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/factories/EventFactory.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $model = Event::class; 16 | 17 | /** 18 | * Define the model's default state. 19 | */ 20 | public function definition(): array 21 | { 22 | return [ 23 | 'action' => 'Create', 24 | 'payload' => [], 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/factories/MediumFactory.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | protected $model = Medium::class; 18 | 19 | /** 20 | * Define the model's default state. 21 | */ 22 | public function definition(): array 23 | { 24 | return [ 25 | 'disk' => 'public', 26 | 'name' => $name = Str::random(5), 27 | 'file_name' => "{$name}.{$this->faker->fileExtension()}", 28 | 'mime_type' => Arr::random(['image/jpg', 'application/pdf']), 29 | 'size' => mt_rand(100, 2000), 30 | 'width' => 1600, 31 | 'height' => 1200, 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/factories/MetaFactory.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $model = Meta::class; 17 | 18 | /** 19 | * Define the model's default state. 20 | */ 21 | public function definition(): array 22 | { 23 | return [ 24 | 'key' => Str::random(5), 25 | 'value' => Str::random(5), 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /database/factories/NotificationFactory.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $model = Notification::class; 17 | 18 | /** 19 | * Define the model's default state. 20 | */ 21 | public function definition(): array 22 | { 23 | return [ 24 | 'type' => 'App\\Notifications\\CustomNotification', 25 | 'subject' => $this->faker->jobTitle(), 26 | 'message' => $this->faker->paragraph(), 27 | 'data' => [], 28 | 'read_at' => Date::now(), 29 | ]; 30 | } 31 | 32 | /** 33 | * Indicate that the model should be unread. 34 | */ 35 | public function unread(): static 36 | { 37 | return $this->state(fn (array $attributes): array => [ 38 | 'read_at' => null, 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /database/factories/SettingFactory.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $model = Setting::class; 16 | 17 | /** 18 | * Define the model's default state. 19 | */ 20 | public function definition(): array 21 | { 22 | return [ 23 | 'key' => $this->faker->slug(1), 24 | 'value' => mt_rand(10, 1000), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/factories/TranslationFactory.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $model = Translation::class; 16 | 17 | /** 18 | * Define the model's default state. 19 | */ 20 | public function definition(): array 21 | { 22 | return [ 23 | 'locale' => $this->faker->locale(), 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/migrations/2020_07_01_000000_create_root_meta_data_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->uuidMorphs('metable'); 17 | $table->string('key')->index(); 18 | $table->text('value')->nullable(); 19 | $table->timestamps(); 20 | 21 | $table->unique(['metable_id', 'metable_type', 'key']); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('root_meta_data'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2023_08_15_101934_create_root_notifications_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 16 | $table->string('type'); 17 | $table->morphs('notifiable'); 18 | $table->string('subject')->index(); 19 | $table->text('message')->nullable(); 20 | $table->json('data')->nullable(); 21 | $table->timestamp('read_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('root_notifications'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/migrations/2024_03_21_175513_create_root_events_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | $table->foreignIdFor(User::getProxiedClass())->nullable()->constrained()->nullOnDelete(); 18 | $table->uuidMorphs('target'); 19 | $table->string('action'); 20 | $table->json('payload')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::drop('root_events'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2024_05_14_091321_create_root_auth_codes_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); 17 | $table->integer('code')->unsigned(); 18 | $table->timestamp('expires_at'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | */ 26 | public function down(): void 27 | { 28 | Schema::dropIfExists('root_auth_codes'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2024_09_02_111321_create_root_settings_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('key')->unique(); 17 | $table->text('value')->nullable(); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('root_settings'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2024_11_07_195330_create_root_translations_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('translatable'); 17 | $table->string('locale', 8); 18 | $table->json('values')->nullable(); 19 | $table->timestamps(); 20 | 21 | $table->unique(['translatable_id', 'translatable_type', 'locale'], 'root_translatable_locale'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('root_translations'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/seeders/RootTestDataSeeder.php: -------------------------------------------------------------------------------- 1 | seedUsers(); 17 | } 18 | 19 | /** 20 | * Seed the user models. 21 | */ 22 | protected function seedUsers(): void 23 | { 24 | User::proxy()->newQuery()->create([ 25 | 'name' => 'Root Admin', 26 | 'email' => 'admin@root.local', 27 | 'password' => Hash::make('password'), 28 | ]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/build/assets/app-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/build/assets/chart-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/build/assets/dropdown-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/build/assets/editor-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/build/assets/media-manager-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/build/assets/repeater-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/build/assets/table-C0jciRh8.js: -------------------------------------------------------------------------------- 1 | import"./app-CAFfX1PR.js"; 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/favicon.png -------------------------------------------------------------------------------- /public/fonts/manrope-v14-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/manrope-v14-latin-500.woff2 -------------------------------------------------------------------------------- /public/fonts/manrope-v14-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/manrope-v14-latin-600.woff2 -------------------------------------------------------------------------------- /public/fonts/manrope-v14-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/manrope-v14-latin-700.woff2 -------------------------------------------------------------------------------- /public/fonts/manrope-v14-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/manrope-v14-latin-800.woff2 -------------------------------------------------------------------------------- /public/fonts/manrope-v14-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/manrope-v14-latin-regular.woff2 -------------------------------------------------------------------------------- /public/fonts/open-sans-v35-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/open-sans-v35-latin-700.woff2 -------------------------------------------------------------------------------- /public/fonts/open-sans-v35-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/fonts/open-sans-v35-latin-regular.woff2 -------------------------------------------------------------------------------- /public/img/auth-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conedevelopment/root/705facdcae1f77e1a6b99555b5129f7cb8362424/public/img/auth-background.png -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import Alpine from 'alpinejs'; 2 | import Axios from 'axios'; 3 | import focus from '@alpinejs/focus'; 4 | import Cookie from '@conedevelopment/qkie'; 5 | import './notifications'; 6 | import './theme'; 7 | import * as Turbo from '@hotwired/turbo'; 8 | 9 | // Turbo 10 | window.Turbo = Turbo; 11 | 12 | // Alpine 13 | Alpine.plugin(focus); 14 | window.Alpine = Alpine; 15 | 16 | // Axios 17 | window.$http = Axios.create({ 18 | headers: { 19 | 'Accept': 'application/json', 20 | 'Content-Type': 'application/json', 21 | 'X-Requested-With': 'XMLHttpRequest', 22 | 'X-Root-Request': 'true', 23 | }, 24 | }); 25 | 26 | // Cookie 27 | window.$cookie = new Cookie('__root_'); 28 | 29 | // Handle the relation frame load event 30 | document.addEventListener('relation-frame-loaded', (event) => { 31 | if (window.location.href !== event.detail.url) { 32 | window.history.replaceState(window.history.state, document.title, event.detail.url); 33 | } 34 | }); 35 | 36 | // Handle the turbo:frame-missing 37 | document.addEventListener('turbo:frame-missing', (event) => { 38 | event.preventDefault(); 39 | }); 40 | -------------------------------------------------------------------------------- /resources/js/chart.js: -------------------------------------------------------------------------------- 1 | import ApexCharts from 'apexcharts'; 2 | 3 | Alpine.data('chart', (config) => { 4 | return { 5 | init() { 6 | const chart = new ApexCharts(this.$el, config); 7 | 8 | chart.render(); 9 | }, 10 | }; 11 | }); 12 | -------------------------------------------------------------------------------- /resources/js/helpers.js: -------------------------------------------------------------------------------- 1 | const debounce = (callback, delay = 300) => { 2 | let timeoutID = null; 3 | 4 | return function () { 5 | clearTimeout(timeoutID); 6 | 7 | const args = arguments; 8 | const context = this; 9 | 10 | timeoutID = setTimeout(function () { 11 | callback.apply(context, args); 12 | }, delay); 13 | }; 14 | }; 15 | 16 | const throttle = (callback, wait = 300) => { 17 | let timeoutID, lastTick; 18 | 19 | return function () { 20 | const args = arguments; 21 | const context = this; 22 | 23 | if (! lastTick) { 24 | callback.apply(context, args); 25 | lastTick = Date.now(); 26 | } else { 27 | clearTimeout(timeoutID); 28 | timeoutID = setTimeout(function () { 29 | if ((Date.now() - lastTick) >= wait) { 30 | callback.apply(context, args); 31 | lastTick = Date.now(); 32 | } 33 | }, wait - (Date.now() - lastTick)); 34 | } 35 | }; 36 | }; 37 | 38 | export { debounce, throttle }; 39 | -------------------------------------------------------------------------------- /resources/js/repeater.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('alpine:init', () => { 2 | window.Alpine.data('repeater', (url, options = []) => { 3 | return { 4 | processing: false, 5 | options: options, 6 | add() { 7 | this.processing = true; 8 | 9 | window.$http.post(url).then((response) => { 10 | this.options.push(response.data); 11 | }).catch((error) => { 12 | // 13 | }).finally(() => { 14 | this.processing = false; 15 | }); 16 | }, 17 | remove(index) { 18 | this.options.splice(index, 1); 19 | }, 20 | swap(from, to) { 21 | const tmp = this.options[to]; 22 | 23 | this.options[to] = this.options[from]; 24 | 25 | this.options[from] = tmp; 26 | }, 27 | }; 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /resources/js/table.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('alpine:init', () => { 2 | window.Alpine.data('table', (config) => { 3 | return { 4 | models: config.models, 5 | selection: [], 6 | selectedAllMatchingQuery: false, 7 | selectedAllModels: false, 8 | init() { 9 | this.$watch('selection', () => { 10 | this.$refs.selectCheckbox.indeterminate = this.selection.length > 0 && this.selection.length < this.models.length; 11 | 12 | this.selectedAllModels = this.selection.length > 0 && this.selection.length === this.models.length; 13 | }); 14 | }, 15 | }; 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /resources/sass/app.scss: -------------------------------------------------------------------------------- 1 | @forward 'config'; 2 | @forward 'layout'; 3 | @forward 'component'; 4 | @forward 'section'; 5 | @forward 'extend'; 6 | @forward 'helper'; 7 | 8 | @use 'sprucecss/scss/spruce' as *; 9 | 10 | :root { 11 | @include set-css-variable(( 12 | --sidebar-inline-size: 20rem, 13 | --header-block-size: 4.5rem, 14 | --container-gap: spacer-clamp('m', 'l') 15 | )); 16 | } 17 | 18 | body { 19 | overflow-x: hidden; 20 | } 21 | 22 | [x-cloak] { 23 | visibility: hidden !important; 24 | } 25 | 26 | img { 27 | border-radius: config('border-radius-sm', $display); 28 | } 29 | -------------------------------------------------------------------------------- /resources/sass/component/_auth-form.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .auth-form { 4 | @include layout-stack('s'); 5 | margin-block: auto; 6 | 7 | &__title { 8 | font-weight: 700; 9 | } 10 | 11 | .or-separator { 12 | margin-block-start: spacer('m'); 13 | } 14 | } 15 | 16 | .form-group-stacked, 17 | .social-logins { 18 | @include layout-stack('s'); 19 | } 20 | 21 | .form-label { 22 | &--space-between { 23 | display: flex; 24 | justify-content: space-between; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /resources/sass/component/_breadcrumb-list.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .breadcrumb-list { 4 | @include clear-list; 5 | align-items: center; 6 | display: flex; 7 | max-inline-size: 100%; 8 | overflow-x: auto; 9 | white-space: nowrap; 10 | 11 | > li { 12 | align-items: center; 13 | display: inline-flex; 14 | margin-block: 0; 15 | 16 | + li::before { 17 | block-size: 0.4em; 18 | border-block-end: 2px solid color('separator', 'breadcrumb'); 19 | border-inline-end: 2px solid color('separator', 'breadcrumb'); 20 | content: ''; 21 | display: inline-flex; 22 | inline-size: 0.4em; 23 | margin-inline: 0.75em; 24 | transform: rotate(-45deg); 25 | 26 | @at-root { 27 | /* stylelint-disable-next-line selector-max-compound-selectors */ 28 | [dir='rtl'] & { 29 | transform: rotate(45deg); 30 | } 31 | } 32 | } 33 | } 34 | 35 | a { 36 | text-decoration: none; 37 | } 38 | 39 | [aria-current='page'] { 40 | @include text-ellipsis(1); 41 | display: inline-block; 42 | max-inline-size: 20ch; 43 | text-align: start; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /resources/sass/component/_data-group.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .data-group { 4 | @include layout-stack('xxs'); 5 | 6 | &__content { 7 | @include text-ellipsis(2); 8 | color: color('heading'); 9 | font-family: config('font-family-heading', $typography); 10 | font-weight: 600; 11 | line-height: config('line-height-heading', $typography); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /resources/sass/component/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'alert'; 2 | @forward 'auth-form'; 3 | @forward 'badge'; 4 | @forward 'block-navigation'; 5 | @forward 'breadcrumb-list'; 6 | @forward 'card'; 7 | @forward 'context-menu'; 8 | @forward 'data-group'; 9 | @forward 'data-table'; 10 | @forward 'form'; 11 | @forward 'media-uploader'; 12 | @forward 'modal'; 13 | @forward 'notification-dot'; 14 | @forward 'notification'; 15 | @forward 'open-search'; 16 | @forward 'or-separator'; 17 | @forward 'pagination'; 18 | @forward 'preloader'; 19 | @forward 'prism'; 20 | @forward 'range-group'; 21 | @forward 'search-modal'; 22 | @forward 'skip-link'; 23 | @forward 'theme-switcher'; 24 | @forward 'user-menu'; 25 | @forward 'welcome-card'; 26 | @forward 'widget'; 27 | -------------------------------------------------------------------------------- /resources/sass/component/_notification-dot.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .notification-dot { 4 | --size: 1em; 5 | background-color: color('success', 'alert'); 6 | block-size: calc(var(--size) / 2); 7 | border-radius: 50%; 8 | display: inline-flex; 9 | inline-size: calc(var(--size) / 2); 10 | margin-left: 10px; 11 | position: relative; 12 | 13 | &::before { 14 | block-size: var(--size); 15 | border: 3px solid color('success', 'alert'); 16 | border-radius: 50%; 17 | content: ''; 18 | inline-size: var(--size); 19 | inset: calc(var(--size) / 4 * -1) auto auto calc(var(--size) / 4 * -1); 20 | opacity: 0; 21 | position: absolute; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /resources/sass/component/_open-search.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .open-search { 4 | @include a11y-card-link('.open-search__btn', true); 5 | align-items: center; 6 | display: flex; 7 | gap: spacer('xs'); 8 | 9 | &__icon { 10 | --size: 1rem; 11 | block-size: var(--size); 12 | color: color('icon', 'search'); 13 | inline-size: var(--size); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /resources/sass/component/_or-separator.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .or-separator { 4 | align-items: center; 5 | display: flex; 6 | font-size: config('font-size-sm', $typography); 7 | gap: spacer('s'); 8 | text-transform: uppercase; 9 | 10 | &::before, 11 | &::after { 12 | background-color: color('border'); 13 | block-size: 1px; 14 | content: ''; 15 | display: flex; 16 | inline-size: 100%; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/sass/component/_pagination.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .pagination { 4 | &__links { 5 | @include clear-list; 6 | display: flex; 7 | flex-wrap: wrap; 8 | gap: spacer('xs'); 9 | 10 | > * + * { 11 | margin-block-start: 0; 12 | } 13 | } 14 | 15 | [aria-current='page'] { 16 | background-color: color('primary-background', 'btn'); 17 | color: color('primary-foreground', 'btn'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/sass/component/_preloader.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .preloader--circle { 4 | --color: currentColor; 5 | --border-width: 0.25em; 6 | --size: 1.5rem; 7 | --animation-duration: 1s; 8 | 9 | block-size: var(--size); 10 | inline-size: var(--size); 11 | 12 | &::after { 13 | animation: rotation var(--animation-duration) linear infinite; 14 | block-size: var(--size); 15 | border: var(--border-width) solid var(--color); 16 | border-color: var(--color) transparent var(--color) transparent; 17 | border-radius: 50%; 18 | content: ''; 19 | display: flex; 20 | inline-size: var(--size); 21 | } 22 | } 23 | 24 | @keyframes rotation { 25 | 0% { transform: rotate(0deg); } 26 | 100% { transform: rotate(360deg); } 27 | } 28 | -------------------------------------------------------------------------------- /resources/sass/component/_range-group.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .range-group { 4 | display: flex; 5 | flex-direction: column; 6 | gap: spacer('xxs'); 7 | 8 | .form-label { 9 | align-items: center; 10 | display: flex; 11 | flex-wrap: wrap; 12 | gap: spacer('xs'); 13 | justify-content: center; 14 | } 15 | 16 | &__inner { 17 | align-items: center; 18 | display: flex; 19 | gap: spacer('xs'); 20 | 21 | .form-range { 22 | flex-grow: 1; 23 | margin-block-start: 0; 24 | } 25 | 26 | .form-range-control { 27 | flex-shrink: 0; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/sass/component/_search-modal.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .search-modal { 4 | display: flex; 5 | flex-direction: column; 6 | gap: spacer('s'); 7 | } 8 | 9 | .search-results { 10 | @include scrollbar; 11 | @include clear-list; 12 | max-block-size: 20rem; 13 | overflow-y: auto; 14 | padding-inline-end: spacer('s'); 15 | 16 | > li + li { 17 | border-block-start: 1px dashed color('border'); 18 | margin-block-start: spacer('xs'); 19 | padding-block-start: spacer('xs'); 20 | } 21 | } 22 | 23 | .search-result-item { 24 | align-items: center; 25 | color: color('text'); 26 | display: flex; 27 | flex-wrap: wrap; 28 | gap: spacer('xs'); 29 | text-decoration: none; 30 | 31 | &__icon { 32 | --size: 1em; 33 | block-size: var(--size); 34 | color: color('primary'); 35 | inline-size: var(--size); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/sass/component/_skip-link.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .skip-link { 4 | inset: -50vh auto auto spacer('m'); 5 | position: fixed; 6 | 7 | &:focus { 8 | inset-block-start: spacer('m'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /resources/sass/component/_theme-switcher.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .no-transition { 4 | * { 5 | transition: none !important; 6 | } 7 | } 8 | 9 | .theme-switcher { 10 | color: color('text'); 11 | display: inline-flex; 12 | position: relative; 13 | 14 | &[data-theme-mode='system'] &__system-mode { display: flex; } 15 | &[data-theme-mode='light'] &__light-mode { display: flex; } 16 | &[data-theme-mode='dark'] &__dark-mode { display: flex; } 17 | 18 | button { 19 | display: none; 20 | 21 | > * { 22 | pointer-events: none; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /resources/sass/component/_user-menu.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .user-menu { 4 | @include a11y-card-link('.user-menu__toggle', true); 5 | align-items: center; 6 | display: flex; 7 | gap: spacer-clamp('xs', 's', '1vw'); 8 | position: relative; 9 | 10 | &__avatar { 11 | --size: 2.1rem; 12 | block-size: var(--size); 13 | border-radius: 50%; 14 | inline-size: var(--size); 15 | } 16 | 17 | &__caption { 18 | display: none; 19 | flex-direction: column; 20 | font-size: config('font-size-base', $typography); 21 | gap: spacer('xxs'); 22 | line-height: 1; 23 | 24 | @include breakpoint('md') { 25 | display: flex; 26 | } 27 | } 28 | 29 | &__role { 30 | font-size: config('font-size-sm', $typography); 31 | } 32 | 33 | &__display-name { 34 | color: color('heading'); 35 | font-weight: 700; 36 | } 37 | 38 | &__toggle { 39 | background: none; 40 | border: 0; 41 | cursor: pointer; 42 | display: flex; 43 | padding: 0; 44 | 45 | svg { 46 | --size: 1em; 47 | block-size: var(--size); 48 | inline-size: var(--size); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /resources/sass/component/_welcome-card.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .welcome-card { 4 | @include a11y-card-link('.welcome-card__link', true); 5 | $this: &; 6 | display: flex; 7 | gap: spacer('m'); 8 | padding: spacer-clamp('m', 2rem); 9 | 10 | &__icon { 11 | --size: 3rem; 12 | align-items: center; 13 | background-color: color('icon-background', 'widget'); 14 | block-size: var(--size); 15 | border-radius: config('border-radius-sm', $display); 16 | color: color('primary'); 17 | display: flex; 18 | flex-shrink: 0; 19 | inline-size: var(--size); 20 | justify-content: center; 21 | 22 | svg { 23 | --size: 1.4rem; 24 | block-size: var(--size); 25 | inline-size: var(--size); 26 | } 27 | } 28 | 29 | &__title { 30 | font-size: font-size('h4'); 31 | font-weight: 600; 32 | margin-block: 0; 33 | } 34 | 35 | &__link { 36 | color: color('heading'); 37 | text-decoration: none; 38 | 39 | &:hover, 40 | &:focus { 41 | color: color('heading'); 42 | } 43 | } 44 | 45 | &__body { 46 | @include layout-stack('xs'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /resources/sass/component/badge/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'trending'; 2 | @forward 'status'; 3 | -------------------------------------------------------------------------------- /resources/sass/component/badge/_status.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:color'; 2 | @use 'sass:map'; 3 | @use 'sprucecss/scss/spruce' as *; 4 | 5 | .status { 6 | align-items: center; 7 | display: inline-flex; 8 | gap: spacer('xs'); 9 | line-height: 1; 10 | position: relative; 11 | white-space: nowrap; 12 | 13 | @each $name, $value in map.get($colors, 'alert') { 14 | &--#{$name}::before { 15 | background-color: color($name, 'alert'); 16 | } 17 | } 18 | 19 | &::before { 20 | --size: 0.55em; 21 | block-size: var(--size); 22 | border-radius: 50%; 23 | content: ''; 24 | flex-shrink: 0; 25 | inline-size: var(--size); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/sass/component/badge/_trending.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:color'; 2 | @use 'sprucecss/scss/spruce' as *; 3 | 4 | .trending { 5 | align-items: center; 6 | border-radius: 2rem; 7 | display: inline-flex; 8 | font-family: config('font-family-heading', $typography); 9 | font-size: config('font-size-sm', $typography); 10 | font-weight: 600; 11 | gap: spacer('xxs'); 12 | line-height: 1; 13 | padding: 0.35em 0.55em; 14 | 15 | &--up { 16 | background: color.adjust(color('success', 'alert', $only-color: true), $lightness: 60%); 17 | color: color.adjust(color('success', 'alert', $only-color: true), $lightness: -7.5%); 18 | } 19 | 20 | &--down { 21 | background: color.adjust(color('danger', 'alert', $only-color: true), $lightness: 45%); 22 | color: color.adjust(color('danger', 'alert', $only-color: true), $lightness: -5%); 23 | } 24 | 25 | &__icon { 26 | --size: 0.95em; 27 | block-size: var(--size); 28 | inline-size: var(--size); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/sass/component/form/_btn-dropdown.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .btn-dropdown { 4 | display: inline-flex; 5 | position: relative; 6 | z-index: 10; 7 | } 8 | -------------------------------------------------------------------------------- /resources/sass/component/form/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'btn-dropdown'; 2 | @forward 'combobox'; 3 | @forward 'editor'; 4 | @forward 'file-group'; 5 | @forward 'file-list'; 6 | @forward 'repeater'; 7 | @forward 'search-form'; 8 | -------------------------------------------------------------------------------- /resources/sass/component/form/_repeater.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .repeater-container { 4 | @include layout-stack('s'); 5 | } 6 | 7 | .repeater { 8 | @include generate-variables($form-control, $include: ('border-width', 'border-radius')); 9 | border: config('border-width', $form-control) solid color('border', 'form'); 10 | border-radius: config('border-radius', $form-control); 11 | padding: spacer('s'); 12 | 13 | &__heading { 14 | align-items: center; 15 | display: flex; 16 | flex-wrap: wrap; 17 | gap: spacer('s'); 18 | justify-content: space-between; 19 | } 20 | 21 | &__body { 22 | border-block-start: 1px solid color('border'); 23 | margin-block-start: spacer('s'); 24 | padding-block-start: spacer('s'); 25 | } 26 | 27 | &__column { 28 | align-items: center; 29 | display: flex; 30 | gap: spacer('s'); 31 | } 32 | 33 | &__title { 34 | @include text-ellipsis(1); 35 | font-size: config('font-size-base', $typography); 36 | margin-block: 0; 37 | max-inline-size: 20ch; 38 | } 39 | 40 | &__actions { 41 | align-items: center; 42 | display: flex; 43 | gap: spacer('xxs'); 44 | } 45 | 46 | &__toggle { 47 | &[aria-expanded='true'] { 48 | .vertical-line { 49 | display: none; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /resources/sass/config/_dark-mode.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | @include generate-color-variables( 4 | $dark-colors, 5 | ':root[data-theme-mode="dark"]' 6 | ); 7 | 8 | [data-theme-mode='dark'] { 9 | color-scheme: dark; 10 | 11 | /* stylelint-disable selector-no-qualifying-type,selector-class-pattern */ 12 | select.form-control:not([multiple]):not([size]), 13 | .combobox__control { 14 | @include field-icon( 15 | config('select', $form-icon, false), 16 | color('select-foreground', 'form', true, $dark-colors) 17 | ); 18 | } 19 | /* stylelint-enable */ 20 | } 21 | -------------------------------------------------------------------------------- /resources/sass/config/_font.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | @include font-face( 4 | 'Manrope', 5 | '/vendor/root/fonts/manrope-v14-latin-regular.woff2' 6 | ); 7 | 8 | @include font-face( 9 | 'Manrope', 10 | '/vendor/root/fonts/manrope-v14-latin-500.woff2', 11 | 500 12 | ); 13 | 14 | @include font-face( 15 | 'Manrope', 16 | '/vendor/root/fonts/manrope-v14-latin-600.woff2', 17 | 600 18 | ); 19 | 20 | @include font-face( 21 | 'Manrope', 22 | '/vendor/root/fonts/manrope-v14-latin-800.woff2', 23 | 700 24 | ); 25 | 26 | @include font-face( 27 | 'Open Sans', 28 | '/vendor/root/fonts/open-sans-v35-latin-regular.woff2' 29 | ); 30 | 31 | @include font-face( 32 | 'Open Sans', 33 | '/vendor/root/fonts/open-sans-v35-latin-700.woff2', 34 | 700 35 | ); 36 | -------------------------------------------------------------------------------- /resources/sass/config/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'config'; 2 | @forward 'font'; 3 | @forward 'styles'; 4 | @forward 'dark-mode'; 5 | -------------------------------------------------------------------------------- /resources/sass/config/_styles.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | @include generate-styles; 4 | -------------------------------------------------------------------------------- /resources/sass/extend/_btn.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:color'; 2 | @use 'sprucecss/scss/spruce' as *; 3 | 4 | .btn--light { 5 | @include btn-variant('light'); 6 | 7 | &.btn--active { 8 | background-color: color('light-background-hover', 'btn'); 9 | border-color: color('light-background-hover', 'btn'); 10 | color: color('light-foreground-hover', 'btn'); 11 | } 12 | } 13 | 14 | .btn--delete { 15 | @include btn-variant('delete'); 16 | } 17 | 18 | .btn--dark { 19 | @include btn-variant('dark'); 20 | } 21 | 22 | .btn--outline-dark { 23 | @include btn-variant-outline('dark'); 24 | } 25 | 26 | .btn { 27 | &--counter { 28 | position: relative; 29 | } 30 | 31 | &__counter { 32 | background-color: color('danger', 'alert'); 33 | border-radius: config('border-radius-sm', $display); 34 | color: color('primary-foreground', 'btn'); 35 | font-size: 0.6375rem; 36 | inset: -0.5em -0.5em auto auto; 37 | min-inline-size: 1.25rem; 38 | padding: 0.45em; 39 | position: absolute; 40 | text-align: center; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /resources/sass/extend/_chart.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .apexcharts-legend-series { 4 | align-items: center; 5 | display: flex !important; 6 | gap: spacer('xxs'); 7 | } 8 | 9 | .apexcharts-legend { 10 | display: flex; 11 | gap: spacer('s'); 12 | 13 | > * { 14 | margin: 0 !important; 15 | } 16 | } 17 | 18 | .apexcharts-text, 19 | .apexcharts-legend-text { 20 | color: color('text') !important; 21 | fill: color('text'); 22 | } 23 | 24 | .apexcharts-tooltip, 25 | .apexcharts-tooltip-title { 26 | background: color('background') !important; 27 | border-color: color('border') !important; 28 | } 29 | 30 | .apexcharts-tooltip-title { 31 | line-height: 1; 32 | padding-block: 0.75em !important; 33 | } 34 | 35 | .apexcharts-xaxis-tick, 36 | .apexcharts-gridline, 37 | .apexcharts-grid-borders line:last-child { 38 | stroke: color('border'); 39 | } 40 | -------------------------------------------------------------------------------- /resources/sass/extend/_form.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .form-group-stack { 4 | @include layout-stack('s'); 5 | 6 | &--bordered { 7 | > * + * { 8 | border-block-start: 1px solid color('border'); 9 | padding-block-start: spacer('s'); 10 | } 11 | } 12 | } 13 | 14 | legend { 15 | font-family: config('font-family-heading', $typography); 16 | } 17 | 18 | .form-control[type=color] { 19 | @include set-css-variable(( 20 | --border-radius: spacer-clamp('m', 'l') 21 | )); 22 | } 23 | 24 | .required-marker { 25 | color: color('danger', 'alert'); 26 | } 27 | -------------------------------------------------------------------------------- /resources/sass/extend/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'btn'; 2 | @forward 'chart'; 3 | @forward 'form'; 4 | @forward 'progress'; 5 | -------------------------------------------------------------------------------- /resources/sass/extend/_progress.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .progressbar { 4 | display: flex; 5 | flex-direction: column; 6 | gap: spacer('xs'); 7 | inline-size: 100%; 8 | 9 | &__inner { 10 | background-color: color('background', 'form'); 11 | block-size: 0.5rem; 12 | border-radius: config('border-radius-sm', $display); 13 | box-shadow: inset 0 0 0 1px color('border', 'form'); 14 | position: relative; 15 | } 16 | 17 | &__indicator { 18 | background-color: color('primary'); 19 | block-size: 100%; 20 | border-radius: config('border-radius-sm', $display); 21 | inset: 0 auto 0 0; 22 | position: absolute; 23 | 24 | &:not([style*='inline-size']) { 25 | animation: 1s progress infinite linear alternate; 26 | inline-size: 20%; 27 | } 28 | } 29 | 30 | &__caption { 31 | text-align: center; 32 | } 33 | } 34 | 35 | @keyframes progress { 36 | 0% { 37 | inset-inline-start: 0%; 38 | } 39 | 40 | 100% { 41 | inset-inline-start: 80%; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /resources/sass/helper/_display.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .display { 4 | @each $name, $breakpoint in $breakpoints { 5 | @include breakpoint($name) { 6 | &--flex\:#{$name} { 7 | display: flex !important; 8 | } 9 | } 10 | } 11 | 12 | @each $name, $breakpoint in $breakpoints { 13 | @include breakpoint($name) { 14 | &--none\:#{$name} { 15 | display: none !important; 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/sass/helper/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'display'; 2 | @forward 'margin'; 3 | @forward 'vertical-align'; 4 | -------------------------------------------------------------------------------- /resources/sass/helper/_margin.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .m-block\:0 { 4 | margin-block: 0 !important; 5 | } 6 | -------------------------------------------------------------------------------- /resources/sass/helper/_vertical-align.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .vertical-align\:top { 4 | vertical-align: top !important; 5 | } 6 | -------------------------------------------------------------------------------- /resources/sass/layout/_container.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .container { 4 | --inline-size: #{config('container-inline-size', $layout)}; 5 | --gap: #{get-css-variable(--container-gap)}; 6 | 7 | @include layout-center( 8 | var(--gap), 9 | var(--inline-size) 10 | ); 11 | 12 | &--wide { 13 | --inline-size: 100%; 14 | } 15 | 16 | &--narrow { 17 | --inline-size: 50rem; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/sass/layout/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'container'; 2 | @forward 'row'; 3 | @forward 'main'; 4 | @forward 'auth'; 5 | -------------------------------------------------------------------------------- /resources/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .l-main { 4 | display: flex; 5 | position: relative; 6 | 7 | &__sidebar { 8 | background-color: color('background'); 9 | display: none; 10 | inline-size: get-css-variable(--sidebar-inline-size); 11 | inset-block: 0; 12 | inset-inline: 0 auto; 13 | position: fixed; 14 | z-index: 20; 15 | 16 | @include breakpoint('md') { 17 | display: block; 18 | } 19 | 20 | &--open { 21 | display: block; 22 | } 23 | } 24 | 25 | &__body { 26 | background-color: color('background', 'main'); 27 | inline-size: 100%; 28 | min-block-size: calc(100lvh + 1rem); 29 | 30 | @include breakpoint('md') { 31 | inline-size: calc(100% - #{get-css-variable(--sidebar-inline-size)}); 32 | margin-inline-start: get-css-variable(--sidebar-inline-size); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /resources/sass/layout/_row.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .l-row { 4 | align-items: start; 5 | display: grid; 6 | gap: spacer('m'); 7 | grid-template-columns: minmax(0, 1fr); 8 | 9 | &--stretch { 10 | align-items: stretch; 11 | } 12 | 13 | @each $name, $breakpoint in $breakpoints { 14 | @for $i from 1 through 4 { 15 | @include breakpoint($name) { 16 | &--column\:#{$name}\:#{$i} { 17 | grid-template-columns: repeat(#{$i}, minmax(0, 1fr)); 18 | } 19 | } 20 | } 21 | } 22 | 23 | &--sidebar { 24 | @include breakpoint('lg') { 25 | grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); 26 | } 27 | } 28 | 29 | &__column { 30 | display: grid; 31 | gap: spacer('m'); 32 | grid-template-columns: minmax(0, 1fr); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /resources/sass/section/_actions.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .app-actions { 4 | justify-content: space-between; 5 | 6 | &--sidebar { 7 | @include breakpoint('lg') { 8 | margin-inline-end: calc(20rem + #{spacer('m')}); 9 | } 10 | } 11 | 12 | &, 13 | &__column { 14 | display: flex; 15 | flex-wrap: wrap; 16 | gap: spacer('m'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/sass/section/_body.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .app-body { 4 | display: flex; 5 | flex-direction: column; 6 | gap: spacer('m'); 7 | } 8 | -------------------------------------------------------------------------------- /resources/sass/section/_footer.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .app-footer { 4 | padding-block: spacer-clamp('m', 'l'); 5 | text-align: center; 6 | 7 | &__created-with { 8 | margin-block: 0; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /resources/sass/section/_header.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .app-header { 4 | backdrop-filter: saturate(180%) blur(0.25rem); 5 | background-color: color('background', 'header'); 6 | border-block-end: 1px solid color('border'); 7 | inset-block-start: 0; 8 | inset-block-start: 0; 9 | position: sticky; 10 | position: sticky; 11 | z-index: 15; 12 | 13 | &__inner { 14 | align-items: center; 15 | block-size: get-css-variable(--header-block-size); 16 | display: flex; 17 | flex-wrap: wrap; 18 | gap: spacer-clamp('s', 'l'); 19 | justify-content: space-between; 20 | margin-inline: get-css-variable(--container-gap); 21 | } 22 | 23 | &__column { 24 | align-items: center; 25 | display: flex; 26 | flex-grow: 1; 27 | gap: spacer-clamp('s', 'm'); 28 | } 29 | 30 | &__actions { 31 | align-items: center; 32 | display: flex; 33 | gap: spacer-clamp('s', 'm'); 34 | 35 | &--secondary { 36 | gap: spacer('s'); 37 | } 38 | } 39 | 40 | &__logo { 41 | block-size: 1.25rem; 42 | display: inline-flex; 43 | 44 | @include breakpoint('md') { 45 | display: none; 46 | } 47 | } 48 | 49 | &__breadcrumb { 50 | display: none; 51 | 52 | @include breakpoint('md') { 53 | display: flex; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /resources/sass/section/_heading.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .app-heading { 4 | padding-block: spacer-clamp('m', 'l'); 5 | 6 | &__inner { 7 | display: flex; 8 | flex-wrap: wrap; 9 | gap: spacer('s') spacer('m'); 10 | justify-content: space-between; 11 | 12 | &--column { 13 | flex-direction: column; 14 | } 15 | } 16 | 17 | &__caption { 18 | @include layout-stack('xs'); 19 | } 20 | 21 | &__title { 22 | font-weight: 700; 23 | } 24 | 25 | &__description { 26 | @include layout-stack('xxs'); 27 | align-items: center; 28 | display: flex; 29 | flex-wrap: wrap; 30 | font-size: 1rem; 31 | gap: spacer('xs') spacer('m'); 32 | } 33 | 34 | &__actions { 35 | align-items: center; 36 | display: flex; 37 | flex-wrap: wrap; 38 | gap: spacer('xs')spacer('s'); 39 | justify-content: end; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /resources/sass/section/_index.scss: -------------------------------------------------------------------------------- 1 | @forward 'sidebar'; 2 | @forward 'header'; 3 | @forward 'heading'; 4 | @forward 'body'; 5 | @forward 'actions'; 6 | @forward 'footer'; 7 | -------------------------------------------------------------------------------- /resources/sass/section/_sidebar.scss: -------------------------------------------------------------------------------- 1 | @use 'sprucecss/scss/spruce' as *; 2 | 3 | .app-sidebar { 4 | block-size: 100%; 5 | border-inline-end: 1px solid color('border'); 6 | display: flex; 7 | flex-direction: column; 8 | gap: spacer('m'); 9 | padding-block: 0 spacer('m'); 10 | 11 | &__logo { 12 | block-size: 1.25rem; 13 | display: inline-flex; 14 | } 15 | 16 | &__search { 17 | display: none; 18 | 19 | @include breakpoint('md') { 20 | display: flex; 21 | } 22 | } 23 | 24 | &__header { 25 | align-items: center; 26 | block-size: get-css-variable(--header-block-size); 27 | border-block-end: 1px solid color('border'); 28 | display: flex; 29 | flex-shrink: 0; 30 | justify-content: space-between; 31 | margin-inline: spacer('m'); 32 | } 33 | 34 | &__body { 35 | @include scrollbar( 36 | $border-radius: 0.15em 37 | ); 38 | flex-grow: 1; 39 | margin-inline: calc(#{spacer('m')} / 2); 40 | overflow-y: auto; 41 | padding-inline: calc(#{spacer('m')} / 2); 42 | 43 | > * + * { 44 | border-block-start: 1px solid color('border'); 45 | margin-block-start: spacer('s'); 46 | padding-block-start: spacer('s'); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /resources/views/actions/action.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @if(! empty($fields)) 3 |
4 | @foreach($fields as $field) 5 | @include($field['template'], $field) 6 | @endforeach 7 |
8 | @endif 9 | 10 |
11 | @csrf 12 | 15 | 18 | 21 | 22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/actions/actions.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 10 | 24 |
25 | -------------------------------------------------------------------------------- /resources/views/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | @extends('root::auth.layout') 2 | 3 | {{-- Title --}} 4 | @section('title', __('Forgot Password')) 5 | 6 | {{-- Content --}} 7 | @section('content') 8 |
9 | @csrf 10 |
11 |
12 | 13 | $errors->has('email')]) 15 | id="email" 16 | type="email" 17 | name="email" 18 | required 19 | value="{{ Request::old('email') }}" 20 | > 21 | @error('email') 22 | {{ $message }} 23 | @enderror 24 |
25 |
26 | 29 |
30 |
31 |
32 | @endsection 33 | -------------------------------------------------------------------------------- /resources/views/components/alert.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/components/chart.blade.php: -------------------------------------------------------------------------------- 1 | @if(! empty($config)) 2 |
3 | @endif 4 | 5 | {{-- Script --}} 6 | @pushOnce('scripts') 7 | {{ 8 | Vite::withEntryPoints('resources/js/chart.js') 9 | ->useBuildDirectory('vendor/root/build') 10 | ->useHotFile(public_path('vendor/root/hot')) 11 | }} 12 | @endpushOnce 13 | -------------------------------------------------------------------------------- /resources/views/components/icon.blade.php: -------------------------------------------------------------------------------- 1 | @include($icon, ['attributes' => $attributes]) 2 | -------------------------------------------------------------------------------- /resources/views/components/layout/breadcrumbs.blade.php: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /resources/views/components/layout/footer.blade.php: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /resources/views/components/layout/sidebar-group.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{ $title }} 4 | 13 |

14 | 28 |
29 | -------------------------------------------------------------------------------- /resources/views/components/layout/theme.blade.php: -------------------------------------------------------------------------------- 1 |
7 | 15 | 23 | 31 |
32 | -------------------------------------------------------------------------------- /resources/views/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | @extends('root::app') 2 | 3 | {{-- Title --}} 4 | @section('title', __('Dashboard')) 5 | 6 | {{-- Content --}} 7 | @section('content') 8 |
9 | @foreach($widgets as $widget) 10 | @include($widget['template'], $widget) 11 | @endforeach 12 |
13 | @endsection 14 | -------------------------------------------------------------------------------- /resources/views/fields/boolean.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 | 12 |
13 | @if($invalid) 14 | {!! $error !!} 15 | @endif 16 | @if($help) 17 | {!! $help !!} 18 | @endif 19 |
20 | -------------------------------------------------------------------------------- /resources/views/fields/checkbox-option.blade.php: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /resources/views/fields/checkbox.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 | @foreach($options as $option) 10 | @include('root::fields.checkbox-option', $option) 11 | @endforeach 12 |
13 | @if($invalid) 14 | {!! $error !!} 15 | @endif 16 | @if($help) 17 | {!! $help !!} 18 | @endif 19 |
20 | -------------------------------------------------------------------------------- /resources/views/fields/date.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 | @if($prefix) 10 |
{!! $prefix !!}
11 | @endif 12 | 13 | @if($suffix) 14 |
{!! $suffix !!}
15 | @endif 16 |
17 | 18 | 21 | 22 | @if($invalid) 23 | {!! $error !!} 24 | @endif 25 | @if($help) 26 | {!! $help !!} 27 | @endif 28 |
29 | -------------------------------------------------------------------------------- /resources/views/fields/dropdown-option.blade.php: -------------------------------------------------------------------------------- 1 | {!! $label !!} 2 | -------------------------------------------------------------------------------- /resources/views/fields/editor/blocks.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 10 | 19 |
20 | -------------------------------------------------------------------------------- /resources/views/fields/editor/heading.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/fields/editor/history.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 11 | 20 |
21 | -------------------------------------------------------------------------------- /resources/views/fields/editor/link.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 11 | 20 |
21 | 22 | {{-- Script --}} 23 | @pushOnce('scripts') 24 | 38 | @endpushOnce 39 | -------------------------------------------------------------------------------- /resources/views/fields/editor/list.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 11 | 20 |
21 | -------------------------------------------------------------------------------- /resources/views/fields/editor/media.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 10 | 11 | @include('root::media.manager', [ 12 | 'label' => $label, 13 | 'modalKey' => $modalKey, 14 | ]) 15 |
16 | -------------------------------------------------------------------------------- /resources/views/fields/fieldset.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ $label }} 4 | @if(empty($fields)) 5 | {{ __('No available fields.') }} 6 | @else 7 | @foreach($fields as $field) 8 | @include($field['template'], $field) 9 | @endforeach 10 | @endif 11 |
12 |
13 | -------------------------------------------------------------------------------- /resources/views/fields/file.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 |
10 | 11 |
12 | 17 |
18 | @if($invalid) 19 | {!! $error !!} 20 | @endif 21 | @if($help) 22 | {!! $help !!} 23 | @endif 24 |
25 | -------------------------------------------------------------------------------- /resources/views/fields/hidden.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/fields/input.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 | @if($prefix) 10 |
{!! $prefix !!}
11 | @endif 12 | 13 | @if($suffix) 14 |
{!! $suffix !!}
15 | @endif 16 |
17 | @if($invalid) 18 | {!! $error !!} 19 | @endif 20 | @if($help) 21 | {!! $help !!} 22 | @endif 23 |
24 | -------------------------------------------------------------------------------- /resources/views/fields/media.blade.php: -------------------------------------------------------------------------------- 1 |
2 | {{ $label }} 3 |
4 | 11 | 16 |
17 | 18 | @include('root::media.manager', [ 19 | 'label' => $label, 20 | 'modalKey' => $modalKey, 21 | 'config' => $config, 22 | ]) 23 |
24 | -------------------------------------------------------------------------------- /resources/views/fields/panel.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

General

4 |
5 |
6 |
7 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /resources/views/fields/repeater-table.blade.php: -------------------------------------------------------------------------------- 1 | @if(! empty($values)) 2 | 3 | 4 | 5 | @foreach($values[0] as $field) 6 | 7 | @endforeach 8 | 9 | 10 | 11 | @foreach($values as $value) 12 | 13 | @foreach($value as $field) 14 | 15 | @endforeach 16 | 17 | @endforeach 18 | 19 |
{!! $field['label'] !!}
{!! $field['formattedValue'] !!}
20 | @endif 21 | -------------------------------------------------------------------------------- /resources/views/fields/repeater.blade.php: -------------------------------------------------------------------------------- 1 |
2 | {{ $label }} 3 |
4 | 7 |
8 |
9 | 18 |
19 |
20 | 21 | {{-- Script --}} 22 | @pushOnce('scripts') 23 | {{ 24 | Vite::withEntryPoints('resources/js/repeater.js') 25 | ->useBuildDirectory('vendor/root/build') 26 | ->useHotFile(public_path('vendor/root/hot')) 27 | }} 28 | @endpushOnce 29 | -------------------------------------------------------------------------------- /resources/views/fields/slug.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 |
9 | @if($prefix) 10 |
{!! $prefix !!}
11 | @endif 12 | 13 | @if($suffix) 14 |
{!! $suffix !!}
15 | @endif 16 | 19 |
20 | @if($invalid) 21 | {!! $error !!} 22 | @endif 23 | @if($help) 24 | {!! $help !!} 25 | @endif 26 |
27 | -------------------------------------------------------------------------------- /resources/views/fields/textarea.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | @if($invalid) 5 | {!! $error !!} 6 | @endif 7 | @if($help) 8 | {!! $help !!} 9 | @endif 10 |
11 | -------------------------------------------------------------------------------- /resources/views/filters/search.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /resources/views/icons/archive.blade.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /resources/views/icons/arrow-outward.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/bar-chart.blade.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /resources/views/icons/bazar-emblem.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/check.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/chevron-down.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/chevron-left.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/chevron-right.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/chevron-up-down.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/chevron-up.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/close.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/cone-emblem.blade.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/views/icons/dark-mode.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/document.blade.php: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /resources/views/icons/download.blade.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/views/icons/edit.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/eye.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/filter.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/format-align-center.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-align-justify.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-align-left.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-align-right.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-bold.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-bullet-list.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-clear.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-highlight.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-italic.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-line.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-link.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-ordered-list.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-strike.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/format-underline.blade.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/views/icons/format-unlink.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/history-do.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/history-undo.blade.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /resources/views/icons/home.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/image.blade.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/views/icons/menu.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/minus.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/notifications.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/pie-chart.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/plus.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/search.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/shopping-bag.blade.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /resources/views/icons/shopping-cart.blade.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /resources/views/icons/spruce-emblem.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/star.blade.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /resources/views/icons/system-mode.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/views/icons/trash.blade.php: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /resources/views/icons/trending-down.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/trending-up.blade.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /resources/views/icons/users.blade.php: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /resources/views/media/filters.blade.php: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /resources/views/media/medium.blade.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /resources/views/media/queued-medium.blade.php: -------------------------------------------------------------------------------- 1 |
  • 5 | 6 |
    7 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 |
  • 22 | -------------------------------------------------------------------------------- /resources/views/resources/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('root::app') 2 | 3 | {{-- Title --}} 4 | @section('title', $title) 5 | 6 | {{-- Actions --}} 7 | @section('actions') 8 | @if(! empty($standaloneActions)) 9 |
    10 | @include('root::actions.actions', ['actions' => $standaloneActions]) 11 |
    12 | @endif 13 | @if($abilities['create']) 14 | 15 | 16 | {{ __('Add :resource', ['resource' => $modelName]) }} 17 | 18 | @endif 19 | @endsection 20 | 21 | {{-- Content --}} 22 | @section('content') 23 | @if(! empty($widgets)) 24 |
    25 | @foreach($widgets as $widget) 26 | @include($widget['template'], $widget) 27 | @endforeach 28 |
    29 | @endif 30 | 31 | @include('root::table.table') 32 | @endsection 33 | -------------------------------------------------------------------------------- /resources/views/resources/relation.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |

    5 | {{ $title }} 6 |

    7 |
    8 | @if($abilities['create']) 9 | 10 | 11 | {{ __('Add :resource', ['resource' => $modelName]) }} 12 | 13 | @endif 14 | @include('root::table.filters') 15 |
    16 |
    17 | @include('root::table.body') 18 |
    19 | 20 | {{-- Script --}} 21 | 24 | @pushOnce('scripts') 25 | {{ 26 | Vite::withEntryPoints('resources/js/table.js') 27 | ->useBuildDirectory('vendor/root/build') 28 | ->useHotFile(public_path('vendor/root/hot')) 29 | }} 30 | @endpushOnce 31 |
    32 | -------------------------------------------------------------------------------- /resources/views/table/actions.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 6 | 9 | {{ __('items selected.') }} 10 | 11 |
    12 |
    13 | 20 | 27 |
    28 |
    29 | @include('root::actions.actions', ['class' => 'btn--sm']) 30 |
    31 |
    32 |
    33 | -------------------------------------------------------------------------------- /resources/views/table/cell.blade.php: -------------------------------------------------------------------------------- 1 | {!! $formattedValue !!} 2 | -------------------------------------------------------------------------------- /resources/views/table/column.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @if($sortable) 3 | @if(Request::input($sortKey.'.by') !== $attribute || Request::input($sortKey.'.order', 'asc') === 'asc') 4 | 10 | {{ $label }} 11 | @if(Request::input($sortKey.'.by') !== $attribute) 12 | 13 | @else 14 | 15 | @endif 16 | 17 | @else 18 | 24 | {{ $label }} 25 | 26 | 27 | @endif 28 | @else 29 | {{ $label }} 30 | @endif 31 | 32 | -------------------------------------------------------------------------------- /resources/views/table/table.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    {{ $title }}

    4 |
    5 | @include('root::table.filters') 6 |
    7 |
    8 | @include('root::table.body') 9 |
    10 | 11 | {{-- Script --}} 12 | @pushOnce('scripts') 13 | {{ 14 | Vite::withEntryPoints('resources/js/table.js') 15 | ->useBuildDirectory('vendor/root/build') 16 | ->useHotFile(public_path('vendor/root/hot')) 17 | }} 18 | @endpushOnce 19 | -------------------------------------------------------------------------------- /resources/views/widgets/trend.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |

    5 | {{ $name }} 6 |

    7 | @if(! is_null($data['current'])) 8 |

    {{ $data['current'] }}

    9 | @endif 10 |
    11 |
    12 |
    13 |
    14 | 15 | 20 |
    21 |
    22 | 23 |
    24 |
    25 |
    26 | -------------------------------------------------------------------------------- /resources/views/widgets/value.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
    3 | @if(! is_null($icon)) 4 |
    5 | 6 |
    7 | @endif 8 |
    9 |

    10 | {{ $name }} 11 |

    12 | @if(! empty($data)) 13 |

    {{ $data['current'] }}

    14 | @if($data['trend'] < 0) 15 | 19 | @elseif($data['trend'] > 0) 20 | 24 | @endif 25 | @endif 26 |
    27 |
    28 |
    29 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | except(['store']); 8 | -------------------------------------------------------------------------------- /routes/auth.php: -------------------------------------------------------------------------------- 1 | name('login'); 11 | Route::post('/login', [LoginController::class, 'login']); 12 | Route::post('/logout', [LoginController::class, 'logout'])->name('logout'); 13 | 14 | // Reset 15 | Route::get('/password/reset', [ForgotPasswordController::class, 'show'])->name('password.request'); 16 | Route::post('/password/email', [ForgotPasswordController::class, 'send'])->name('password.email'); 17 | Route::get('/password/reset/{token}/{email}', [ResetPasswordController::class, 'show'])->name('password.reset'); 18 | Route::post('/password/reset', [ResetPasswordController::class, 'reset'])->name('password.update'); 19 | 20 | // Two Factor Verification 21 | Route::get('/two-factor', [TwoFactorController::class, 'show'])->name('two-factor.show'); 22 | Route::post('/two-factor', [TwoFactorController::class, 'verify'])->name('two-factor.verify'); 23 | Route::post('/two-factor/resend', [TwoFactorController::class, 'resend'])->name('two-factor.resend'); 24 | -------------------------------------------------------------------------------- /src/Actions/SendPasswordResetNotification.php: -------------------------------------------------------------------------------- 1 | each(static function (User $user) use ($broker): void { 20 | $user->sendPasswordResetNotification( 21 | $broker->createToken($user) 22 | ); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Actions/SendVerificationNotification.php: -------------------------------------------------------------------------------- 1 | reject(static fn (User $user): bool => $user->hasVerifiedEmail())->each(static function (User $user): void { 17 | $user->sendEmailVerificationNotification(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Casts/MetaValue.php: -------------------------------------------------------------------------------- 1 | $attributes 15 | */ 16 | public function get(Model $model, string $key, mixed $value, array $attributes): mixed 17 | { 18 | return is_null($value) ? $value : (json_decode($value, true) ?? $value); 19 | } 20 | 21 | /** 22 | * Prepare the given value for storage. 23 | * 24 | * @param array $attributes 25 | */ 26 | public function set(Model $model, string $key, mixed $value, array $attributes): string|false|null 27 | { 28 | return match (true) { 29 | is_null($value) => null, 30 | is_string($value), is_numeric($value), $value instanceof Stringable => (string) $value, 31 | default => json_encode($value) ?: (string) $value, 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Console/Commands/ActionMake.php: -------------------------------------------------------------------------------- 1 | allFiles(Config::get('root.media.upload_dir')) as $file) { 38 | $info = new SplFileInfo(Storage::disk('local')->path($file)); 39 | 40 | if ($now - $info->getMTime() >= $expiration) { 41 | Storage::disk('local')->delete($file); 42 | 43 | $count++; 44 | } 45 | } 46 | 47 | $this->info(sprintf('%d chunks are cleared!', $count)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Console/Commands/ClearMedia.php: -------------------------------------------------------------------------------- 1 | newQuery()->cursor()->each(static function (Medium $medium) use (&$count): void { 32 | $medium->delete(); 33 | 34 | $count++; 35 | }); 36 | 37 | $this->info(sprintf('%d media have been deleted!', $count)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Commands/Install.php: -------------------------------------------------------------------------------- 1 | call('migrate'); 33 | 34 | File::ensureDirectoryExists(Config::get('root.media.tmp_dir')); 35 | 36 | if ($this->option('seed')) { 37 | $this->call('db:seed', ['--class' => RootTestDataSeeder::class]); 38 | } 39 | 40 | $this->call('vendor:publish', [ 41 | '--provider' => RootServiceProvider::class, 42 | '--tag' => 'root-stubs', 43 | ]); 44 | 45 | $this->info('Root has been installed.'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Console/Commands/TrendMake.php: -------------------------------------------------------------------------------- 1 | createImage($medium); 28 | 29 | call_user_func_array($callback, [$image, $medium]); 30 | 31 | $image->save(); 32 | 33 | File::move($image->getPath(), $medium->getAbsolutePath($conversion)); 34 | 35 | $image->destroy(); 36 | } 37 | 38 | /** 39 | * Create a new image instance. 40 | */ 41 | protected function createImage(Medium $medium): Image 42 | { 43 | return tap(new Image($medium), function (Image $image): void { 44 | $image->setQuality($this->config['quality'] ?? 70); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Conversion/Manager.php: -------------------------------------------------------------------------------- 1 | config->get('root.media.conversion.default'); 22 | } 23 | 24 | /** 25 | * Register a new conversion. 26 | */ 27 | public function registerConversion(string $name, Closure $callback): void 28 | { 29 | $this->conversions[$name] = $callback; 30 | } 31 | 32 | /** 33 | * Remove the given conversion. 34 | */ 35 | public function removeConversion(string $name): void 36 | { 37 | unset($this->conversions[$name]); 38 | } 39 | 40 | /** 41 | * Get all the registered conversions. 42 | */ 43 | public function getConversions(): array 44 | { 45 | return $this->conversions; 46 | } 47 | 48 | /** 49 | * Create the GD driver. 50 | */ 51 | public function createGdDriver(): GdDriver 52 | { 53 | return new GdDriver( 54 | $this->config->get('root.media.conversion.drivers.gd', []) 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Exceptions/QueryResolutionException.php: -------------------------------------------------------------------------------- 1 | class('form-check__control') 35 | ->setAttributes([ 36 | 'type' => 'checkbox', 37 | 'name' => sprintf('%s[]', $this->getModelAttribute()), 38 | ]); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Fields/Color.php: -------------------------------------------------------------------------------- 1 | type('color'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Fields/Email.php: -------------------------------------------------------------------------------- 1 | type('email'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Fields/HasMany.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class HasMany extends HasOneOrMany 13 | { 14 | /** 15 | * Create a new relation field instance. 16 | */ 17 | public function __construct(string $label, Closure|string|null $modelAttribute = null, Closure|string|null $relation = null) 18 | { 19 | parent::__construct($label, $modelAttribute, $relation); 20 | 21 | $this->setAttribute('multiple', true); 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function getRelation(Model $model): EloquentRelation 28 | { 29 | return parent::getRelation($model); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Fields/HasOne.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class HasOne extends HasOneOrMany 12 | { 13 | /** 14 | * {@inheritdoc} 15 | */ 16 | public function getRelation(Model $model): EloquentRelation 17 | { 18 | return parent::getRelation($model); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Fields/Hidden.php: -------------------------------------------------------------------------------- 1 | type('hidden'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Fields/ID.php: -------------------------------------------------------------------------------- 1 | hiddenOn(['create', 'update']); 15 | 16 | $this->sortable(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Fields/MorphMany.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class MorphMany extends MorphOneOrMany 12 | { 13 | /** 14 | * {@inheritdoc} 15 | */ 16 | public function getRelation(Model $model): EloquentRelation 17 | { 18 | return parent::getRelation($model); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Fields/MorphOne.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class MorphOne extends MorphOneOrMany 14 | { 15 | /** 16 | * {@inheritdoc} 17 | */ 18 | public function getRelation(Model $model): EloquentRelation 19 | { 20 | return parent::getRelation($model); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Fields/MorphOneOrMany.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | abstract class MorphOneOrMany extends HasOneOrMany 14 | { 15 | /** 16 | * {@inheritdoc} 17 | */ 18 | public function getRelation(Model $model): EloquentRelation 19 | { 20 | return parent::getRelation($model); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Fields/Number.php: -------------------------------------------------------------------------------- 1 | type('number'); 17 | } 18 | 19 | /** 20 | * Set the "min" HTML attribute. 21 | */ 22 | public function min(int|float|Closure $value): static 23 | { 24 | return $this->setAttribute('min', $value); 25 | } 26 | 27 | /** 28 | * Set the "max" HTML attribute. 29 | */ 30 | public function max(int|float|Closure $value): static 31 | { 32 | return $this->setAttribute('max', $value); 33 | } 34 | 35 | /** 36 | * Set the "step" HTML attribute. 37 | */ 38 | public function step(int|float|Closure $value): static 39 | { 40 | return $this->setAttribute('step', $value); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Fields/Radio.php: -------------------------------------------------------------------------------- 1 | class('form-check__control') 14 | ->setAttributes([ 15 | 'type' => 'radio', 16 | 'name' => $this->getModelAttribute(), 17 | ]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Fields/Range.php: -------------------------------------------------------------------------------- 1 | type('range'); 22 | $this->step(1); 23 | $this->min(0); 24 | $this->max(100); 25 | $this->class(['form-range', 'range-group__control']); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Fields/Text.php: -------------------------------------------------------------------------------- 1 | type('text'); 17 | } 18 | 19 | /** 20 | * Set the "size" HTML attribute. 21 | */ 22 | public function size(int|Closure $value): static 23 | { 24 | return $this->setAttribute('size', $value); 25 | } 26 | 27 | /** 28 | * Set the "minlength" HTML attribute. 29 | */ 30 | public function minlength(int|Closure $value): static 31 | { 32 | return $this->setAttribute('minlength', $value); 33 | } 34 | 35 | /** 36 | * Set the "maxlength" HTML attribute. 37 | */ 38 | public function maxlength(int|Closure $value): static 39 | { 40 | return $this->setAttribute('maxlength', $value); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Fields/Textarea.php: -------------------------------------------------------------------------------- 1 | class(['form-control']); 22 | } 23 | 24 | /** 25 | * Set the rows attribute. 26 | */ 27 | public function rows(int|Closure $value): static 28 | { 29 | return $this->setAttribute('rows', $value); 30 | } 31 | 32 | /** 33 | * Set the cols attribute. 34 | */ 35 | public function cols(int|Closure $value): static 36 | { 37 | return $this->setAttribute('cols', $value); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Filters/MediaSearch.php: -------------------------------------------------------------------------------- 1 | attributes = $attributes; 22 | 23 | parent::__construct(new Fields); 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function getSearchableAttributes(): array 30 | { 31 | return array_fill_keys($this->attributes, null); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Filters/RenderableFilter.php: -------------------------------------------------------------------------------- 1 | fields = $fields; 23 | 24 | parent::__construct(); 25 | } 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function apply(Request $request, Builder $query, mixed $value): Builder 31 | { 32 | if (empty($value)) { 33 | return $query; 34 | } 35 | 36 | return $query->where(function (Builder $query) use ($request, $value): void { 37 | $this->fields->each(function (Field $field) use ($request, $query, $value): void { 38 | $field->resolveSearchQuery($request, $query, $value); 39 | }); 40 | }); 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function toField(): SearchField 47 | { 48 | return SearchField::make($this->getName(), $this->getRequestKey()) 49 | ->value(fn (Request $request): ?string => $this->getValue($request)) 50 | ->placeholder(sprintf('%s...', $this->getName())); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Filters/SearchField.php: -------------------------------------------------------------------------------- 1 | classList()->replace('form-control', 'search-form__control'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Filters/TrashStatus.php: -------------------------------------------------------------------------------- 1 | getModel()))) { 17 | return $query; 18 | } 19 | 20 | return match ($value) { 21 | 'all' => $query->withTrashed(), 22 | 'trashed' => $query->onlyTrashed(), 23 | default => $query, 24 | }; 25 | } 26 | 27 | /** 28 | * Determine if the filter is active. 29 | */ 30 | public function isActive(Request $request): bool 31 | { 32 | return parent::isActive($request) 33 | && $this->getValue($request) !== 'available'; 34 | } 35 | 36 | /** 37 | * Get the filter options. 38 | */ 39 | public function options(Request $request): array 40 | { 41 | return [ 42 | 'available' => __('Available'), 43 | 'trashed' => __('Trashed'), 44 | 'all' => __('All'), 45 | ]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Http/Controllers/ActionController.php: -------------------------------------------------------------------------------- 1 | route('action'); 17 | 18 | return $action->perform($request); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | validate(['email' => ['required', 'string', 'email']]); 31 | 32 | Password::broker()->sendResetLink($data, static function (User $user, #[\SensitiveParameter] string $token): void { 33 | $user->notify(new ResetPassword($token)); 34 | }); 35 | 36 | return Redirect::route('root.auth.password.request')->with('status', __(Password::RESET_LINK_SENT)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | $root->widgets->map->data($request)->all(), 19 | ]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Controllers/DownloadController.php: -------------------------------------------------------------------------------- 1 | middleware('signed'); 16 | } 17 | 18 | /** 19 | * Handle the incoming request. 20 | */ 21 | public function __invoke(Medium $medium): BinaryFileResponse 22 | { 23 | return $medium->download(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Http/Controllers/MediaController.php: -------------------------------------------------------------------------------- 1 | route('_resource'); 18 | 19 | /** @var \Cone\Root\Fields\Media $field */ 20 | $field = $request->route('field'); 21 | 22 | $model = $request->filled('model') 23 | ? $resource->resolveRouteBinding($request, $request->input('model')) 24 | : $resource->getModelInstance(); 25 | 26 | return match ($request->method()) { 27 | 'GET' => new JsonResponse($field->paginateRelatable($request, $model)), 28 | 'POST' => new JsonResponse($field->upload($request, $model), JsonResponse::HTTP_CREATED), 29 | 'DELETE' => new JsonResponse(['deleted' => $field->prune($request, $model, $request->input('ids', []))]), 30 | default => throw new MethodNotAllowedHttpException(['GET', 'POST', 'DELETE']), 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Http/Controllers/MorphToController.php: -------------------------------------------------------------------------------- 1 | route('field'); 18 | 19 | $data = $field->toInput($request, $model); 20 | 21 | return ResponseFactory::view($data['template'], $data); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/Controllers/RepeaterController.php: -------------------------------------------------------------------------------- 1 | route('_resource'); 17 | 18 | /** @var \Cone\Root\Fields\Repeater $field */ 19 | $field = $request->route('field'); 20 | 21 | $model = $request->filled('model') 22 | ? $resource->resolveRouteBinding($request, $request->input('model')) 23 | : $resource->getModelInstance(); 24 | 25 | return new JsonResponse($field->buildOption($request, $model)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Http/Controllers/WidgetController.php: -------------------------------------------------------------------------------- 1 | route('widget'); 18 | 19 | $data = $widget->data($request); 20 | 21 | return ResponseFactory::view($data['template'], $data); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson() ? null : URL::route('root.auth.login'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authorize.php: -------------------------------------------------------------------------------- 1 | route($name)->authorized( 21 | $request, 22 | ...array_values(Arr::only($request->route()->parameters(), $parameters)) 23 | )); 24 | 25 | return $next($request); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Http/Middleware/TwoFactorAuthenticate.php: -------------------------------------------------------------------------------- 1 | user()->shouldTwoFactorAuthenticate($request)) { 20 | return Redirect::route('root.auth.two-factor.show'); 21 | } 22 | 23 | return $next($request); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Interfaces/Breadcrumbs/Registry.php: -------------------------------------------------------------------------------- 1 | medium = $medium; 38 | } 39 | 40 | /** 41 | * Execute the job. 42 | */ 43 | public function handle(): void 44 | { 45 | $this->medium->convert(); 46 | } 47 | 48 | /** 49 | * Handle a job failure. 50 | */ 51 | public function failed(Throwable $exception): void 52 | { 53 | $this->medium->delete(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Listeners/FormatRootStubs.php: -------------------------------------------------------------------------------- 1 | tag === 'root-stubs') { 16 | foreach ($event->paths as $to) { 17 | $contents = file_get_contents($to); 18 | 19 | $contents = str_replace('{{ namespace }}', App::getNamespace(), $contents); 20 | 21 | file_put_contents($to, $contents); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Models/Attachment.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $casts = [ 15 | 'meta' => 'json', 16 | ]; 17 | 18 | /** 19 | * The attributes that are mass assignable. 20 | * 21 | * @var list 22 | */ 23 | protected $fillable = [ 24 | 'collection', 25 | 'meta', 26 | ]; 27 | 28 | /** 29 | * The table associated with the model. 30 | * 31 | * @var string 32 | */ 33 | protected $table = 'root_mediables'; 34 | } 35 | -------------------------------------------------------------------------------- /src/Navigation/Location.php: -------------------------------------------------------------------------------- 1 | name = $name; 20 | } 21 | 22 | /** 23 | * Get the items by their groups. 24 | */ 25 | public function groups(): array 26 | { 27 | return array_reduce($this->items, static function (array $groups, Item $item): array { 28 | $groups[$item->group ?? __('General')][] = $item; 29 | 30 | return $groups; 31 | }, []); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Navigation/Registry.php: -------------------------------------------------------------------------------- 1 | locations[$name] ??= new Location($name); 20 | } 21 | 22 | /** 23 | * Get the registered. locations 24 | */ 25 | public function locations(): array 26 | { 27 | return $this->locations; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Notifications/ResetPassword.php: -------------------------------------------------------------------------------- 1 | $this->token, 21 | 'email' => $notifiable->getEmailForPasswordReset(), 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Notifications/RootChannel.php: -------------------------------------------------------------------------------- 1 | $notification::class], 16 | $notification->toRoot($notifiable)->toArray() 17 | ); 18 | 19 | $notifiable->rootNotifications()->create($data); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Notifications/RootNotification.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function via(object $notifiable): array 19 | { 20 | return [RootChannel::class]; 21 | } 22 | 23 | /** 24 | * Get the Root Message representation of the notification. 25 | */ 26 | abstract public function toRoot(object $notifiable): RootMessage; 27 | } 28 | -------------------------------------------------------------------------------- /src/Settings/Registry.php: -------------------------------------------------------------------------------- 1 | repository = $repository; 21 | } 22 | 23 | /** 24 | * Get the repository instance. 25 | */ 26 | public function getRepository(): Repository 27 | { 28 | return $this->repository; 29 | } 30 | 31 | /** 32 | * Dynamically call the given method. 33 | */ 34 | public function __call(string $name, array $arguments): mixed 35 | { 36 | return call_user_func_array([$this->repository, $name], $arguments); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Support/Facades/Conversion.php: -------------------------------------------------------------------------------- 1 | registerConversion($name, $callback); 30 | } 31 | 32 | /** 33 | * Remove the given conversion. 34 | */ 35 | public static function remove(string $name): void 36 | { 37 | static::getFacadeRoot()->removeConversion($name); 38 | } 39 | 40 | /** 41 | * Get all the registered conversions. 42 | */ 43 | public static function all(): array 44 | { 45 | return static::getFacadeRoot()->getConversions(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Traits/AsForm.php: -------------------------------------------------------------------------------- 1 | validateWithBag( 35 | $this->errorBag, 36 | $this->resolveFields($request)->mapToValidate($request, $model) 37 | ); 38 | } 39 | 40 | /** 41 | * Get the errors. 42 | */ 43 | public function errors(Request $request): MessageBag 44 | { 45 | if (is_null($this->errors)) { 46 | $this->errors = $request->session()->get('errors', new ViewErrorBag)->getBag($this->errorBag); 47 | } 48 | 49 | return $this->errors; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Traits/Authorizable.php: -------------------------------------------------------------------------------- 1 | authorizationResolver = $callback; 21 | 22 | return $this; 23 | } 24 | 25 | /** 26 | * Resolve the authorization. 27 | */ 28 | public function authorized(Request $request, ...$parameters): bool 29 | { 30 | return is_null($this->authorizationResolver) 31 | || call_user_func_array($this->authorizationResolver, [$request, ...$parameters]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Traits/Filterable.php: -------------------------------------------------------------------------------- 1 | except('filter') as $name => $value) { 16 | if ($this->hasNamedScope($name)) { 17 | $this->callNamedScope($name, [$query, $value]); 18 | } 19 | } 20 | 21 | return $query; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Traits/HasMedia.php: -------------------------------------------------------------------------------- 1 | forceDeleting || ! in_array(SoftDeletes::class, class_uses_recursive($model))) { 19 | $model->media()->detach(); 20 | } 21 | }); 22 | } 23 | 24 | /** 25 | * Get the media for the model. 26 | */ 27 | public function media(): MorphToMany 28 | { 29 | return $this->morphToMany(Medium::getProxiedClass(), 'mediable', 'root_mediables') 30 | ->as('attachment') 31 | ->using(Attachment::class) 32 | ->withPivot(['meta', 'collection']) 33 | ->withTimestamps(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Traits/HasMetaData.php: -------------------------------------------------------------------------------- 1 | morphMany(Meta::getProxiedClass(), 'metable'); 16 | } 17 | 18 | /** 19 | * Get the meta value of the given key. 20 | */ 21 | public function metaValue(string $key, mixed $default = null): mixed 22 | { 23 | return $this->metaData->firstWhere('key', $key)?->value ?: $default; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Traits/HasRootEvents.php: -------------------------------------------------------------------------------- 1 | morphMany(Event::getProxiedClass(), 'target'); 17 | } 18 | 19 | /** 20 | * Record a new root event for the model. 21 | */ 22 | public function recordRootEvent(string $action, ?User $user = null, ?array $payload = null): Event 23 | { 24 | $event = $this->rootEvents()->make([ 25 | 'action' => $action, 26 | 'payload' => $payload, 27 | ]); 28 | 29 | $event->user()->associate($user)->save(); 30 | 31 | return $event; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Traits/InteractsWithProxy.php: -------------------------------------------------------------------------------- 1 | make( 25 | static::getProxiedInterface() 26 | ); 27 | } catch (Throwable) { 28 | $proxy = new static; 29 | } 30 | } 31 | 32 | return $proxy; 33 | } 34 | 35 | /** 36 | * Get the proxied class. 37 | */ 38 | public static function getProxiedClass(): string 39 | { 40 | return static::proxy()::class; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Traits/Makeable.php: -------------------------------------------------------------------------------- 1 | widgets)) { 30 | $this->widgets = new Widgets($this->widgets($request)); 31 | 32 | $this->widgets->each(function (Widget $widget) use ($request): void { 33 | $this->resolveWidget($request, $widget); 34 | }); 35 | } 36 | 37 | return $this->widgets; 38 | } 39 | 40 | /** 41 | * Handle the callback for the widget resolution. 42 | */ 43 | protected function resolveWidget(Request $request, Widget $widget): void 44 | { 45 | // 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Traits/Translatable.php: -------------------------------------------------------------------------------- 1 | morphMany(Translation::getProxiedClass(), 'translatable'); 17 | } 18 | 19 | /** 20 | * Translate the value of the given key. 21 | */ 22 | public function translate(string $key, ?string $locale = null): mixed 23 | { 24 | $locale ??= App::getLocale(); 25 | 26 | return match ($locale) { 27 | (Translation::proxy())::getTranslatableLocale() => $this->getAttribute($key), 28 | default => $this->translations->firstWhere('locale', $locale)?->values[$key] ?? null, 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/View/Components/Alert.php: -------------------------------------------------------------------------------- 1 | type = $type; 26 | $this->closable = $closable; 27 | } 28 | 29 | /** 30 | * Get the view / view contents that represent the component. 31 | */ 32 | public function render(): View 33 | { 34 | return $this->view('root::components.alert', [ 35 | 'type' => $this->type, 36 | 'closable' => $this->closable, 37 | 'class' => match ($this->type) { 38 | 'error' => 'alert--danger', 39 | default => 'alert--'.$this->type, 40 | }, 41 | ]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/View/Components/Chart.php: -------------------------------------------------------------------------------- 1 | config = $config; 21 | } 22 | 23 | /** 24 | * Get the view / view contents that represent the component. 25 | */ 26 | public function render(): View 27 | { 28 | return $this->view('root::components.chart', [ 29 | 'config' => $this->config, 30 | ]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/View/Components/Icon.php: -------------------------------------------------------------------------------- 1 | name = $name; 21 | } 22 | 23 | /** 24 | * Get the view / view contents that represent the component. 25 | */ 26 | public function render(): View 27 | { 28 | return $this->view('root::components.icon', [ 29 | 'icon' => sprintf('root::icons.%s', $this->name), 30 | ]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/View/Components/Layout/Breadcrumbs.php: -------------------------------------------------------------------------------- 1 | request = $request; 23 | } 24 | 25 | /** 26 | * Get the view / view contents that represent the component. 27 | */ 28 | public function render(): View 29 | { 30 | return $this->view('root::components.layout.breadcrumbs', [ 31 | 'breadcrumbs' => Root::instance()->breadcrumbs->resolve($this->request), 32 | ]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/View/Components/Layout/Footer.php: -------------------------------------------------------------------------------- 1 | view('root::components.layout.footer', [ 17 | 'version' => Root::VERSION, 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/View/Components/Layout/Header.php: -------------------------------------------------------------------------------- 1 | view('root::components.layout.header'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/View/Components/Layout/Notifications.php: -------------------------------------------------------------------------------- 1 | view('root::components.layout.notifications', [ 17 | 'url' => URL::route('root.api.notifications.index'), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/View/Components/Layout/Sidebar.php: -------------------------------------------------------------------------------- 1 | view('root::components.layout.sidebar', [ 17 | 'groups' => Root::instance()->navigation->location('sidebar')->groups(), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/View/Components/Layout/SidebarGroup.php: -------------------------------------------------------------------------------- 1 | title = $title; 26 | $this->items = $items; 27 | } 28 | 29 | /** 30 | * Get the view / view contents that represent the component. 31 | */ 32 | public function render(): View 33 | { 34 | return $this->view('root::components.layout.sidebar-group', [ 35 | 'title' => $this->title, 36 | 'items' => $this->items, 37 | ]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/View/Components/Layout/Theme.php: -------------------------------------------------------------------------------- 1 | view('root::components.layout.theme', [ 17 | 'theme' => Cookie::get('__root_theme', 'system'), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/View/Components/Modal.php: -------------------------------------------------------------------------------- 1 | title = $title; 37 | $this->subtitle = $subtitle; 38 | $this->key = strtolower($key ?: Str::random()); 39 | $this->open = $open; 40 | } 41 | 42 | /** 43 | * Get the view / view contents that represent the component. 44 | */ 45 | public function render(): View 46 | { 47 | return $this->view('root::components.modal', [ 48 | 'key' => $this->key, 49 | 'subtitle' => $this->subtitle, 50 | 'title' => $this->title, 51 | 'open' => $this->open, 52 | ]); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Widgets/Welcome.php: -------------------------------------------------------------------------------- 1 | User::class, 19 | ]; 20 | 21 | /** 22 | * Register any application services. 23 | */ 24 | public function register(): void 25 | { 26 | // 27 | } 28 | 29 | /** 30 | * Bootstrap any application services. 31 | */ 32 | public function boot(): void 33 | { 34 | Gate::define('viewRoot', static function (User $user): bool { 35 | return false; 36 | }); 37 | 38 | Root::instance()->resources->register([ 39 | new UserResource(), 40 | ]); 41 | 42 | Root::instance()->widgets->register([ 43 | new Welcome(), 44 | ]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /stubs/SelectFilter.stub: -------------------------------------------------------------------------------- 1 | count($request, $this->resolveQuery($request)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /stubs/Value.stub: -------------------------------------------------------------------------------- 1 | count($request, $this->resolveQuery($request)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /stubs/Widget.stub: -------------------------------------------------------------------------------- 1 |