├── resources ├── views │ ├── .gitkeep │ ├── calendar │ │ ├── event-list.blade.php │ │ ├── month.blade.php │ │ ├── week.blade.php │ │ ├── event.blade.php │ │ └── day.blade.php │ ├── layout │ │ ├── page.blade.php │ │ └── heading.blade.php │ ├── widgets │ │ └── mini │ │ │ ├── event-widget-empty.blade.php │ │ │ ├── day-widget.blade.php │ │ │ └── event-widget.blade.php │ ├── event │ │ └── view.blade.php │ └── header │ │ └── header.blade.php ├── svg │ ├── txt.svg │ ├── zip.svg │ ├── day-1.svg │ ├── ppt.svg │ ├── pdf.svg │ ├── day-7.svg │ ├── pptx.svg │ ├── day-4.svg │ ├── xls.svg │ ├── day-11.svg │ ├── png.svg │ ├── xlsx.svg │ ├── day-5.svg │ ├── day-2.svg │ ├── day-17.svg │ ├── day-14.svg │ ├── day-6.svg │ ├── jpg.svg │ ├── day-9.svg │ ├── day-10.svg │ ├── day-8.svg │ ├── doc.svg │ ├── day-3.svg │ ├── day-21.svg │ ├── day-15.svg │ ├── day-12.svg │ ├── docx.svg │ ├── day-16.svg │ ├── day-27.svg │ ├── day-19.svg │ ├── day-24.svg │ ├── day-18.svg │ ├── day-20.svg │ ├── day-31.svg │ ├── day-13.svg │ ├── day-25.svg │ ├── day-22.svg │ ├── day-26.svg │ ├── day-30.svg │ ├── day-29.svg │ ├── day-28.svg │ ├── day-23.svg │ └── timex.svg ├── lang │ ├── en │ │ └── timex.php │ ├── es │ │ └── timex.php │ └── ru │ │ └── timex.php └── dist │ ├── timex.css │ └── timex.js ├── src ├── Timex.php ├── Calendar │ ├── Header.php │ ├── Week.php │ ├── Day.php │ ├── Event.php │ ├── EventList.php │ └── Month.php ├── Facades │ └── Timex.php ├── Commands │ ├── stubs │ │ └── AttTable.stub │ └── MakeAttachmentsTableCommand.php ├── Models │ ├── Category.php │ └── Event.php ├── Widgets │ └── Mini │ │ ├── DayWidget.php │ │ └── EventWidget.php ├── Traits │ ├── Uuids.php │ └── TimexTrait.php ├── Resources │ ├── EventResource │ │ └── Pages │ │ │ ├── CreateEvent.php │ │ │ ├── EditEvent.php │ │ │ └── ListEvents.php │ └── EventResource.php ├── Events │ ├── InteractWithEvents.php │ └── EventItem.php ├── TimexServiceProvider.php └── Pages │ └── Timex.php ├── database ├── factories │ └── ModelFactory.php └── migrations │ └── create_timex_tables.php.stub ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── timex.php └── configure.php /resources/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/calendar/event-list.blade.php: -------------------------------------------------------------------------------- 1 |
2 | {{ $this->table }} 3 |
4 | -------------------------------------------------------------------------------- /src/Timex.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | {{-- WIP 6 |
--}} 7 | {{-- --}} 8 | {{--
--}} 9 | 10 | -------------------------------------------------------------------------------- /src/Calendar/Header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | {{trans('timex::timex.events.empty', ['label' => Str::lower(trans('timex::timex.model.pluralLabel'))])}} 5 |
6 | 7 | -------------------------------------------------------------------------------- /src/Calendar/Week.php: -------------------------------------------------------------------------------- 1 | json('attachments')->nullable(); 13 | }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/Models/Category.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/zip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `Timex` will be documented in this file. 4 | 5 | ## 1.0.3 - 2022-11-23 6 | 7 | ### What's Changed 8 | 9 | - Translations, config, redirect to timex page by @mikrosmile in https://github.com/buildix/timex/pull/6 10 | 11 | **Full Changelog**: https://github.com/buildix/timex/compare/1.0.2...1.0.3 12 | 13 | ## 1.0.2 - 2022-11-22 14 | 15 | ### What's Changed 16 | 17 | - Introducing TiMEX Mini Widget - day view & upcoming events 18 | 19 | **Full Changelog**: https://github.com/buildix/timex/compare/1.0.1...1.0.2 20 | 21 | ## TiMEX - Calendar plugin for filament - 2022-11-22 22 | 23 | Nothing here for now, just initial release 24 | -------------------------------------------------------------------------------- /resources/views/layout/heading.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @unless(config('timex.mini.isDayViewHidden')) 3 |
!config('timex.mini.isNextMeetingViewHidden') 5 | ])> 6 | 7 |
8 | @endunless 9 | @unless(config('timex.mini.isNextMeetingViewHidden')) 10 |
15 | 16 |
17 | @endunless 18 |
19 | -------------------------------------------------------------------------------- /resources/views/widgets/mini/day-widget.blade.php: -------------------------------------------------------------------------------- 1 |
8 |
9 | {{$monthName.'.'}} 10 |
11 |
12 |
13 | {{$day}} 14 |
15 |
16 | {{$dayName}} 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /resources/views/calendar/month.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
4 | @foreach(collect($this->getDays())['weekDays'] as $dayOfWeek) 5 |
6 | 13 |
14 | @endforeach 15 |
16 | {{-- --}} 17 |
18 | -------------------------------------------------------------------------------- /src/Widgets/Mini/DayWidget.php: -------------------------------------------------------------------------------- 1 | monthName = today()->shortMonthName; 18 | $this->day = today()->day; 19 | $this->dayName = today()->shortDayName; 20 | } 21 | 22 | public function render() 23 | { 24 | return view('timex::widgets.mini.day-widget'); 25 | } 26 | 27 | public function openCalendar() 28 | { 29 | return \Redirect::to(config('FILAMENT_PATH').'/'.$this->getPageClass()::getSlug()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/views/widgets/mini/event-widget.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @foreach($events as $event) 3 |
4 | 16 |
17 | @endforeach 18 |
19 | -------------------------------------------------------------------------------- /resources/svg/day-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/calendar/week.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{$name}} 4 |
5 |
$last, 10 | 'border-r dark:border-gray-600' => !$last, 11 | 'bg-gray-50 dark:bg-gray-700' => in_array($dayOfWeek,\Carbon\Carbon::getWeekendDays()), 12 | ] 13 | ) 14 | {{-- @if($last)--}} 15 | {{-- style="border-bottom-right-radius: 0.75rem;"--}} 16 | {{-- @endif--}} 17 | > 18 | @foreach($days as $day) 19 | 25 | @endforeach 26 |
27 |
28 | -------------------------------------------------------------------------------- /src/Traits/Uuids.php: -------------------------------------------------------------------------------- 1 | {$model->getKeyName()})) { 15 | $model->{$model->getKeyName()} = Str::uuid(); 16 | } 17 | }); 18 | } 19 | /** 20 | * Get the value indicating whether the IDs are incrementing. 21 | * 22 | * @return bool 23 | */ 24 | public function getIncrementing() 25 | { 26 | return false; 27 | } 28 | /** 29 | * Get the auto-incrementing key type. 30 | * 31 | * @return string 32 | */ 33 | public function getKeyType() 34 | { 35 | return 'string'; 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/Resources/EventResource/Pages/CreateEvent.php: -------------------------------------------------------------------------------- 1 | schema(self::getResource()::getCreateEditForm()); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /resources/svg/ppt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lang/en/timex.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'label' => 'Event', 6 | 'pluralLabel' => 'Events', 7 | ], 8 | 'modal' => [ 9 | 'submit' => 'Submit', 10 | 'cancel' => 'Cancel', 11 | 'delete' => 'Delete', 12 | 'edit' => 'Edit', 13 | ], 14 | 'events' => [ 15 | 'empty' => 'No upcoming :label' 16 | ], 17 | 'event' => [ 18 | 'subject' => 'Subject', 19 | 'body' => 'Body', 20 | 'category' => 'Category', 21 | 'allDay' => 'All day', 22 | 'start' => 'Start', 23 | 'end' => 'End', 24 | 'participants' => 'Participants', 25 | 'attachments' => 'Attachments', 26 | 27 | ], 28 | 'event-list' => [ 29 | 'author' => 'Author: :name', 30 | 'start' => 'Start: :start', 31 | 'end' => 'End: :end' 32 | ], 33 | 'labels' => [ 34 | 'navigation' => 'TiMEX', 35 | 'breadcrumbs' => 'TiMEX', 36 | 'title' => 'TiMEX', 37 | 'today' => 'Today', 38 | ], 39 | ]; 40 | -------------------------------------------------------------------------------- /resources/svg/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lang/es/timex.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'label' => 'Evento', 6 | 'pluralLabel' => 'Eventos', 7 | ], 8 | 'modal' => [ 9 | 'submit' => 'Enviar', 10 | 'cancel' => 'Cancelar', 11 | 'delete' => 'Borrar', 12 | 'edit' => 'Editar', 13 | ], 14 | 'events' => [ 15 | 'empty' => 'Sin :label' 16 | ], 17 | 'event' => [ 18 | 'subject' => 'Tema', 19 | 'body' => 'Evento', 20 | 'category' => 'Categoria', 21 | 'allDay' => 'Todo el dia', 22 | 'start' => 'Inicio', 23 | 'end' => 'Final', 24 | 'participants' => 'Participantes', 25 | 'attachments' => 'Archivos adjuntos', 26 | 27 | ], 28 | 'event-list' => [ 29 | 'author' => 'Autor: :nombre', 30 | 'start' => 'Inicio: :inicio', 31 | 'end' => 'Final: :final' 32 | ], 33 | 'labels' => [ 34 | 'navigation' => 'Calendario', 35 | 'breadcrumbs' => 'Calendario', 36 | 'title' => 'Calendario', 37 | 'today' => 'Hoy', 38 | ], 39 | ]; 40 | -------------------------------------------------------------------------------- /resources/lang/ru/timex.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'label' => 'Событие', 6 | 'pluralLabel' => 'События', 7 | ], 8 | 'modal' => [ 9 | 'submit' => 'Сохранить', 10 | 'cancel' => 'Отмена', 11 | 'delete' => 'Удалить', 12 | 'edit' => 'Редактировать', 13 | ], 14 | 'events' => [ 15 | 'empty' => 'Нет событий' 16 | ], 17 | 'event' => [ 18 | 'subject' => 'Тема', 19 | 'body' => 'Описание', 20 | 'category' => 'Категория', 21 | 'allDay' => 'Целый день', 22 | 'start' => 'Начало', 23 | 'end' => 'Окончание', 24 | 'participants' => 'Участники', 25 | 'attachments' => 'Вложения', 26 | ], 27 | 'event-list' => [ 28 | 'author' => 'Автор: :name', 29 | 'start' => 'Начало: :start', 30 | 'end' => 'Окончание: :end' 31 | ], 32 | 'labels' => [ 33 | 'navigation' => 'TiMEX', 34 | 'breadcrumbs' => 'TiMEX', 35 | 'title' => 'TiMEX', 36 | 'today' => 'Сегодня', 37 | ], 38 | 39 | ]; 40 | -------------------------------------------------------------------------------- /resources/svg/day-7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/pptx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/day-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/dist/timex.css: -------------------------------------------------------------------------------- 1 | .timex-month { 2 | display: grid; 3 | grid-template-columns: repeat(7, minmax(0, 1fr)); 4 | text-align: center; 5 | background: white; 6 | border-radius: 0.75rem; 7 | border-width: 1px; 8 | } 9 | 10 | .timex-week-name { 11 | font-weight: 500; 12 | padding: 0.5rem; 13 | height: 2.5rem; 14 | 15 | } 16 | 17 | .timex-week{ 18 | grid-auto-flow: column; 19 | } 20 | 21 | .timex-week-last { 22 | border-bottom-right-radius: 0.66rem; 23 | } 24 | 25 | .timex-day { 26 | align-items: center; 27 | text-align: left; 28 | height: 65px; 29 | } 30 | 31 | @media (min-width: 640px) { 32 | .timex-day { 33 | align-items: center; 34 | text-align: left; 35 | height: 65px; 36 | } 37 | 38 | } 39 | 40 | @media (min-width: 768px) { 41 | .timex-day { 42 | align-items: center; 43 | text-align: left; 44 | height: 65px; 45 | } 46 | 47 | } 48 | 49 | @media (min-width: 1024px) { 50 | .timex-day { 51 | align-items: center; 52 | text-align: left; 53 | height: 130px; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Models/Event.php: -------------------------------------------------------------------------------- 1 | 'date', 21 | 'end' => 'date', 22 | 'isAllDay' => 'boolean', 23 | 'participants' => 'array', 24 | 'attachments' => 'array', 25 | ]; 26 | 27 | public function getTable() 28 | { 29 | return config('timex.tables.event.name', "timex_events"); 30 | } 31 | 32 | public function __construct(array $attributes = []) 33 | { 34 | $attributes['organizer'] = \Auth::id(); 35 | 36 | parent::__construct($attributes); 37 | 38 | } 39 | 40 | public function category() 41 | { 42 | return $this->hasOne(self::getCategoryModel()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Buildix 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Resources/EventResource/Pages/EditEvent.php: -------------------------------------------------------------------------------- 1 | schema(self::getResource()::getCreateEditForm()); 29 | } 30 | 31 | protected function getActions(): array 32 | { 33 | return [ 34 | DeleteAction::make(), 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Resources/EventResource/Pages/ListEvents.php: -------------------------------------------------------------------------------- 1 | where('organizer','=',\Auth::id()) 31 | ->orWhereJsonContains('participants', \Auth::id()); 32 | }else{ 33 | return parent::getTableQuery(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /resources/dist/timex.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('monthLoaded', event => { 2 | const days = event.detail.fullDays; 3 | days.forEach(function (day){ 4 | Sortable.create(document.getElementById(day['id']),{ 5 | group: { 6 | name: "shared", 7 | }, 8 | animation: 150, 9 | sort: false, 10 | setData: function (dataTransfer, dragEl) { 11 | dataTransfer.setData('id', dragEl.id); 12 | }, 13 | onStart: function (evt){ 14 | }, 15 | onChoose: function (/**Event*/evt) { 16 | }, 17 | onMove: function (evt){ 18 | let element = document.getElementById(evt.to.id); 19 | }, 20 | onEnd: function (evt) { 21 | const sameDate = evt.from === evt.to; 22 | if (sameDate) { 23 | return; 24 | } 25 | const eventId = evt.item.id; 26 | const fromDate = evt.from.dataset.statusId; 27 | const toDateID = evt.to.dataset.statusId 28 | Livewire.emit('onEventChanged',eventId,toDateID) 29 | }, 30 | }) 31 | }) 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /resources/svg/xls.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/day-11.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/png.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/xlsx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/day-5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![timex-logo](https://user-images.githubusercontent.com/2136612/202689778-eb013a03-b0fa-4c0e-941c-7d999c09fd6f.jpeg) 2 | 3 | 4 | ## TiMEX - calendar plugin for [filament](https://github.com/filamentphp/filament) 5 | 6 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/buildix/timex.svg?style=flat-square)](https://packagist.org/packages/buildix/timex) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/buildix/timex.svg?style=flat-square)](https://packagist.org/packages/buildix/timex) 8 | 9 | ![IMG_4796](https://user-images.githubusercontent.com/117465609/213705414-873d6122-ecda-459f-954f-d3b14cfee2e8.JPG) 10 | 11 | ## Support 12 | 13 | Join Discord server to receive quick support: [Buildix](https://discord.gg/EuHKhSrf78) 14 | ## Installation & use 15 | 16 | * [Installation](docs/01-install.md) 17 | * [Config](docs/02-config.md) 18 | * [Page configuration](docs/03-page.md) 19 | * [Buttons configuration](docs/04-buttons.md) 20 | * [Filament resource configuration](docs/05-resource.md) 21 | * [TiMEX Categories configuration](docs/06-categories.md) 22 | 23 | 24 | ## Demo 25 | 26 | [![buildix-timex demo](https://img.youtube.com/vi/ojtwJvEU-RI/0.jpg)](https://www.youtube.com/watch?v=ojtwJvEU-RI) 27 | 28 | ## Credits 29 | 30 | - [mikrosmile](https://github.com/mikrosmile) 31 | - [All Contributors](../../contributors) 32 | 33 | ## License 34 | 35 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 36 | -------------------------------------------------------------------------------- /resources/svg/day-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-17.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-14.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/jpg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/day-9.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /database/migrations/create_timex_tables.php.stub: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 13 | $table->json('attachments')->nullable(); 14 | $table->longText('body')->nullable(); 15 | $table->string('category')->nullable(); 16 | $table->date('end'); 17 | $table->time('endTime')->nullable(); 18 | $table->boolean('isAllDay')->default(false); 19 | $table->foreignUuid('organizer'); 20 | $table->json('participants')->nullable(); 21 | $table->longText('subject'); 22 | $table->date('start'); 23 | $table->time('startTime')->nullable(); 24 | 25 | $table->timestamps(); 26 | }); 27 | 28 | Schema::create(config('timex.tables.category.name'), function (Blueprint $table){ 29 | $table->uuid('id')->primary(); 30 | $table->string('value'); 31 | $table->string('icon')->nullable(); 32 | $table->string('color')->nullable(); 33 | }); 34 | 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function down() 43 | { 44 | Schema::dropIfExists(config('timex.tables.event.name')); 45 | Schema::dropIfExists(config('timex.tables.category.name')); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /resources/svg/day-10.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Widgets/Mini/EventWidget.php: -------------------------------------------------------------------------------- 1 | 'updateWidget' 23 | ]; 24 | 25 | public function updateWidget() 26 | { 27 | $this->reset('events'); 28 | $this->events = self::getEvents(); 29 | } 30 | 31 | public function boot() 32 | { 33 | $this->now = Carbon::today()->timestamp; 34 | } 35 | 36 | public function mount() 37 | { 38 | $this->events = self::getEvents(); 39 | } 40 | 41 | public static function getEvents(): Collection 42 | { 43 | $events = self::getPageClass()::getEvents(); 44 | 45 | return collect($events)->filter(function ($event){ 46 | return isset($event->startTime) && !config('timex.resources.isStartEndHidden',false) ? $event->start == today()->timestamp && Carbon::createFromTimeString($event->startTime) >= now() : $event->start == today()->timestamp 47 | || $event->start == today()->timestamp && $event->isAllDay; 48 | }); 49 | 50 | } 51 | 52 | public function render() 53 | { 54 | if (count($this->events) == 0){ 55 | return view('timex::widgets.mini.event-widget-empty'); 56 | }else{ 57 | return view('timex::widgets.mini.event-widget'); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/Calendar/Day.php: -------------------------------------------------------------------------------- 1 | events = $this->getEvents($this->timestamp); 28 | 29 | $this->isCurrentDay = \Carbon\Carbon::createFromTimestamp($this->timestamp)->isCurrentDay(); 30 | $this->isCurrentMonthDay = \Carbon\Carbon::createFromTimestamp($this->timestamp)->isCurrentMonth(); 31 | $this->isWeekend = \Carbon\Carbon::createFromTimestamp($this->timestamp)->isWeekend(); 32 | $this->firstDayOfMonth = \Carbon\Carbon::createFromTimestamp($this->timestamp)->firstOfMonth()->timestamp; 33 | $this->isFirstOfMonth = $this->timestamp == $this->firstDayOfMonth; 34 | } 35 | 36 | 37 | public function getEvents($timespamp): Collection 38 | { 39 | $events = collect(Timex::getEvents()) 40 | ->sortBy(function ($event){ 41 | $event->startTime; 42 | }); 43 | return collect($events)->filter(function ($events) use ($timespamp){ 44 | return $this->eventInDay($events->start,$timespamp); 45 | 46 | }); 47 | } 48 | 49 | public function render() 50 | { 51 | return view('timex::calendar.day'); 52 | } 53 | 54 | protected function eventInDay($event,$timespamp) 55 | { 56 | return $event == $timespamp; 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /resources/svg/doc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/day-3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-21.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-15.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Calendar/Event.php: -------------------------------------------------------------------------------- 1 | isMyEvent = $this->organizer == \Auth::id() ? true : false; 30 | $this->isModelEnabled = self::isCategoryModelEnabled() && \Str::isUuid($this->category) ? true : false; 31 | $model = $this->isModelEnabled ? $this->getModelData() : null; 32 | if ($this->isModelEnabled){ 33 | $this->icon = $model[self::getCategoryModelColumn('icon')]; 34 | $this->color = $model[self::getCategoryModelColumn('color')]; 35 | 36 | }elseif (!$this->isModelEnabled && \Str::isUuid($this->category)){ 37 | $this->icon = ""; 38 | $this->color = "primary"; 39 | }else{ 40 | $this->icon = config('timex.categories.icons.'.$this->category); 41 | $this->color = config('timex.categories.colors.'.$this->color); 42 | } 43 | 44 | $eventStart = \Carbon\Carbon::createFromTimestamp($this->start)->setHours(23); 45 | $this->isInPast = $eventStart->isPast(); 46 | 47 | } 48 | 49 | public function getModelData() 50 | { 51 | return $model = self::getCategoryModel()::query()->find($this->category)->getAttributes(); 52 | } 53 | 54 | 55 | public function render() 56 | { 57 | return view('timex::calendar.event'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resources/svg/day-12.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/docx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/svg/day-16.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-27.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-19.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-24.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-18.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Events/InteractWithEvents.php: -------------------------------------------------------------------------------- 1 | record); 39 | return $record; 40 | } 41 | 42 | public function eventUpdated($data) 43 | { 44 | $event = self::getModel()::query()->find($data['id']); 45 | $eventData = $event->getAttributes(); 46 | $end = Carbon::create($eventData['end']); 47 | $toDate = Carbon::createFromTimestamp($data['toDate']); 48 | 49 | if ($eventData['organizer'] == \Auth::id() && (($toDate->isAfter(today()) || $toDate->isCurrentDay())) || config('timex.isPastCreationEnabled', false)){ 50 | $event->update([ 51 | 'start' => Carbon::createFromTimestamp($data['toDate']), 52 | ]); 53 | if ($end < $toDate){ 54 | $event->update([ 55 | 'end' => $toDate 56 | ]); 57 | } 58 | } 59 | $this->emit('modelUpdated',['id' => $this->id]); 60 | $this->emit('updateWidget',['id' => $this->id]); 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buildix/timex", 3 | "description": "Calendar plugin for filamentphp", 4 | "keywords": [ 5 | "Buildix", 6 | "laravel", 7 | "timex", 8 | "filament" 9 | ], 10 | "homepage": "https://github.com/buildix/timex", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Mikhail Karzanov", 15 | "email": "mikrosmile@icloud.com", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.1", 21 | "spatie/laravel-package-tools": "^1.13.0", 22 | "filament/filament": "^v2.16.55" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.0", 26 | "nunomaduro/collision": "^6.0", 27 | "orchestra/testbench": "^7.0", 28 | "pestphp/pest": "^1.21", 29 | "pestphp/pest-plugin-laravel": "^1.1", 30 | "phpunit/phpunit": "^9.5" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Buildix\\Timex\\": "src", 35 | "Buildix\\Timex\\Database\\Factories\\": "database/factories" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Buildix\\Timex\\Tests\\": "tests" 41 | } 42 | }, 43 | "scripts": { 44 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 45 | "analyse": "vendor/bin/phpstan analyse", 46 | "test": "vendor/bin/pest", 47 | "test-coverage": "vendor/bin/pest --coverage", 48 | "format": "vendor/bin/pint" 49 | }, 50 | "config": { 51 | "sort-packages": true, 52 | "allow-plugins": { 53 | "pestphp/pest-plugin": true, 54 | "phpstan/extension-installer": true 55 | } 56 | }, 57 | "extra": { 58 | "laravel": { 59 | "providers": [ 60 | "Buildix\\Timex\\TimexServiceProvider" 61 | ], 62 | "aliases": { 63 | "Timex": "Buildix\\Timex\\Facades\\Timex" 64 | } 65 | } 66 | }, 67 | "minimum-stability": "dev", 68 | "prefer-stable": true 69 | } 70 | -------------------------------------------------------------------------------- /resources/svg/day-20.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-31.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/calendar/event.blade.php: -------------------------------------------------------------------------------- 1 |
3 | $color != 'secondary', 8 | 'bg-gray-600' => $color == 'secondary', 9 | 'hidden' => $isWidgetEvent, 10 | ])> 11 | 12 | 13 |
!$isMyEvent && !$isWidgetEvent, 16 | 'grid grid-cols-7 items-center text-left text-xs font-light cursor-pointer', 17 | 'w-full rounded ml-1 mr-1', 18 | 'hover:bg-'.$color.'-600/20' => $color !== 'secondary' && !$isWidgetEvent, 19 | 'hover:bg-gray-600/20' => $color == 'secondary' && !$isWidgetEvent, 20 | 'text-white hover:text-'.$color.'-500 bg-'.$color.'-500' => $color != 'secondary' && !$isInPast && !$isWidgetEvent && $isMyEvent, 21 | 'text-white hover:text-gray-500 bg-gray-500' => $color == 'secondary' && !$isInPast && !$isWidgetEvent && $isMyEvent, 22 | ]) 23 | > 24 | 25 | @if($icon) 26 |
27 | $isWidgetEvent && $color !== 'secondary', 32 | 'text-gray-500' => $isWidgetEvent && $color == 'secondary', 33 | ]) 34 | /> 35 |
36 | @endif 37 |
!$icon, 39 | 'col-span-4 truncate' => $icon, 40 | ])> 41 | {{$subject}} 42 |
43 |
!$isWidgetEvent, 45 | 'col-span-2 ml-4 truncate' => $isWidgetEvent 46 | ])> 47 | @if($isAllDay || config('timex.resources.isStartEndHidden',false)) 48 | {{config('timex.resources.isStartEndHidden',false) ? '' : trans('timex::timex.event.allDay')}} 49 | @else 50 | {{\Carbon\Carbon::parse($startTime)->isoFormat('H:mm')}} 51 | @endif 52 |
53 |
54 | 55 |
56 | -------------------------------------------------------------------------------- /resources/svg/day-13.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-25.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-22.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-26.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-30.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-29.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/svg/day-28.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Traits/TimexTrait.php: -------------------------------------------------------------------------------- 1 | today = $this->getToday(); 18 | } 19 | 20 | /** 21 | * @return Carbon 22 | */ 23 | public function getToday(): Carbon 24 | { 25 | return Carbon::today(); 26 | } 27 | 28 | public function getCurrMonth(?Carbon $currMonth): static 29 | { 30 | return $this->currMonth = $currMonth ? $currMonth : today()->month; 31 | } 32 | 33 | public function getStartOfWeek() 34 | { 35 | return $this->startOfWeek = config('timex.week.start'); 36 | } 37 | 38 | public function getEndOfWeek() 39 | { 40 | return $this->endOfWeek = config('timex.week.end');; 41 | } 42 | 43 | public static function getPageClass() 44 | { 45 | return config('timex.pages.timex'); 46 | } 47 | 48 | public static function getUserModel() 49 | { 50 | return config('timex.models.users.model'); 51 | } 52 | 53 | public static function getEventTableName() 54 | { 55 | return config('timex.tables.event.name'); 56 | } 57 | 58 | public static function getUserModelColumn($column) 59 | { 60 | return match ($column){ 61 | 'name' => config('timex.models.users.name'), 62 | 'id' => config('timex.models.users.id') 63 | }; 64 | } 65 | 66 | public static function getDynamicLabel(string $label) 67 | { 68 | $format = match ($label){ 69 | 'navigation' => config('timex.pages.label.navigation.format'), 70 | 'breadcrumbs' => config('timex.pages.label.breadcrumbs.format'), 71 | 'title' => config('timex.pages.label.title.format'), 72 | 'today' => config('timex.pages.buttons.today.format'), 73 | }; 74 | 75 | return Carbon::today()->isoFormat($format); 76 | } 77 | 78 | public static function getCategoryModel() 79 | { 80 | return config('timex.categories.model.class'); 81 | } 82 | 83 | public static function getCategoryModelColumn(string $column) 84 | { 85 | return match ($column){ 86 | 'key' => config('timex.categories.model.key'), 87 | 'value' => config('timex.categories.model.value'), 88 | 'icon' => config('timex.categories.model.icon'), 89 | 'color' => config('timex.categories.model.color') 90 | }; 91 | } 92 | 93 | public static function isCategoryModelEnabled(): bool 94 | { 95 | return config('timex.categories.isModelEnabled'); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /resources/svg/day-23.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/calendar/day.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
!$isCurrentMonthDay || $isWeekend, 5 | 'border-t dark:border-gray-600', 6 | 'pl-2 pt-2', 7 | ]) 8 | wire:click="$emitUp('onDayClick','{{$timestamp}}')"> 9 | $isCurrentDay, 15 | 'rounded-full px-3 h-6', 16 | 'hover:bg-gray-500 hover:text-white' 17 | ] 18 | ) 19 | > 20 | 23 | {{$day}} 24 | 25 | 26 | @unless(!$isFirstOfMonth) 27 | 28 |
33 | {{\Carbon\Carbon::createFromTimestamp($timestamp)->shortMonthName}} 34 |
35 |
36 | @endunless 37 |
38 | 63 |
64 | @foreach(collect($events)->take(4) as $event) 65 | getColor().'-500' => $event->getColor() != 'secondary', 70 | 'bg-gray-600' => $event->getColor() == 'secondary', 71 | ])> 72 | 73 | @endforeach 74 |
75 |
76 | -------------------------------------------------------------------------------- /src/TimexServiceProvider.php: -------------------------------------------------------------------------------- 1 | __DIR__.'/../resources/dist/timex.js' 31 | ]; 32 | 33 | protected array $styles = [ 34 | 'timex' => __DIR__.'/../resources/dist/timex.css' 35 | ]; 36 | 37 | public function configurePackage(Package $package): void 38 | { 39 | /* 40 | * This class is a Package Service Provider 41 | * 42 | * More info: https://github.com/spatie/laravel-package-tools 43 | */ 44 | $package 45 | ->name('timex') 46 | ->hasConfigFile() 47 | ->hasViews() 48 | ->hasAssets() 49 | ->hasTranslations() 50 | ->hasMigration('create_timex_tables') 51 | ->hasCommands([ 52 | MakeAttachmentsTableCommand::class 53 | ]) 54 | ->hasInstallCommand(function (InstallCommand $command){ 55 | $command 56 | ->publishConfigFile() 57 | ->publishMigrations() 58 | ->askToRunMigrations() 59 | ->askToStarRepoOnGitHub('buildix/timex'); 60 | }); 61 | } 62 | 63 | public function boot() 64 | { 65 | Livewire::component('timex-month',Month::class); 66 | Livewire::component('timex-week',Week::class); 67 | Livewire::component('timex-day',Day::class); 68 | Livewire::component('timex-event',Event::class); 69 | Livewire::component('timex-event-widget',EventWidget::class); 70 | Livewire::component('timex-day-widget',DayWidget::class); 71 | Livewire::component('timex-event-list',EventList::class); 72 | Livewire::component('timex-header',Header::class); 73 | 74 | $this->registerConfig(); 75 | 76 | $this->callAfterResolving(Factory::class, function (Factory $factory, Container $container) { 77 | $config = $container->make('config')->get('timex', []); 78 | 79 | $factory->add('timex', array_merge(['path' => __DIR__.'/../resources/svg'], $config)); 80 | }); 81 | 82 | if (config('timex.mini.isMiniCalendarEnabled')){ 83 | Filament::registerRenderHook( 84 | 'global-search.start', 85 | fn(): View => \view('timex::layout.heading') 86 | ); 87 | } 88 | 89 | parent::boot(); 90 | } 91 | 92 | private function registerConfig(): void 93 | { 94 | $this->mergeConfigFrom(__DIR__.'/../config/timex.php', 'timex'); 95 | } 96 | 97 | protected function getPages(): array 98 | { 99 | return [ 100 | config('timex.pages.timex') 101 | ]; 102 | } 103 | protected function getResources(): array 104 | { 105 | return [ 106 | config('timex.resources.event') 107 | ]; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Calendar/EventList.php: -------------------------------------------------------------------------------- 1 | 'onTodayClick', 32 | // 'onPrevClick' => 'onPrevClick', 33 | // 'onNextClick' => 'onNextClick' 34 | // ]; 35 | 36 | public function boot() 37 | { 38 | $this->start = Carbon::create($this->today); 39 | $this->end = Carbon::create($this->today)->endOfMonth(); 40 | } 41 | 42 | public function onTodayClick() 43 | { 44 | $this->today = Carbon::today(); 45 | $this->start = Carbon::create($this->today); 46 | $this->end = Carbon::create($this->today)->endOfMonth(); 47 | } 48 | 49 | public function onPrevClick() 50 | { 51 | $this->today = $this->today->subMonth(); 52 | $this->start = Carbon::create($this->today)->firstOfMonth(); 53 | $this->end = Carbon::create($this->today)->endOfMonth(); 54 | } 55 | 56 | public function onNextClick() 57 | { 58 | $this->today = $this->today->addMonth(); 59 | $this->start = Carbon::create($this->today)->firstOfMonth(); 60 | $this->end = Carbon::create($this->today)->endOfMonth(); 61 | } 62 | 63 | protected static function getEventModel(): string 64 | { 65 | return static::$model = config('timex.models.event'); 66 | } 67 | 68 | protected static function getEventResource(): string 69 | { 70 | return static::$recource = config('timex.resources.event'); 71 | } 72 | 73 | protected function getTableQuery(): Builder|Relation 74 | { 75 | return self::getEventModel()::whereBetween('start', [$this->start, $this->end]); 76 | } 77 | 78 | public function render() 79 | { 80 | return view('timex::calendar.event-list'); 81 | } 82 | 83 | protected function getTableColumns(): array 84 | { 85 | return [ 86 | TextColumn::make('subject')->label(__('timex::timex.event.subject')) 87 | ->description(function ($record){ 88 | return __('timex::timex.event-list.author',['name' => self::getUserModel()::find($record->organizer)->getAttribute(self::getUserModelColumn('name'))]); 89 | })->wrap(), 90 | TextColumn::make('start')->date()->description(function ($record){ 91 | return __('timex::timex.event-list.start',['start' => Carbon::create($record->startTime)->format('G:i')]); 92 | })->label(__('timex::timex.event.start')), 93 | TextColumn::make('end')->date()->description(function ($record){ 94 | return __('timex::timex.event-list.end',['end' => Carbon::create($record->endTime)->format('G:i')]); 95 | })->label(__('timex::timex.event.end')), 96 | ]; 97 | } 98 | 99 | protected function getTableRecordUrlUsing(): ?Closure 100 | { 101 | return fn (Model $record): string => $this->getEventResource()::getUrl('edit', ['record' => $record]); 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/Events/EventItem.php: -------------------------------------------------------------------------------- 1 | eventID($eventID); 31 | } 32 | 33 | public function body(?string $body): static 34 | { 35 | $this->body = $body; 36 | 37 | return $this; 38 | } 39 | 40 | public function category(?string $category): static 41 | { 42 | $this->category = $category; 43 | 44 | return $this; 45 | } 46 | 47 | public function color(?string $color): static 48 | { 49 | $this->color = $color; 50 | 51 | return $this; 52 | } 53 | 54 | public function end(Carbon $end): static 55 | { 56 | $this->end = $end->setHour(0)->setMinute(0)->setSeconds(0)->timestamp; 57 | 58 | return $this; 59 | } 60 | 61 | public function eventID($eventID) 62 | { 63 | $this->eventID = $eventID; 64 | 65 | return $this; 66 | } 67 | 68 | public static function make($eventID): static 69 | { 70 | return app(static::class, ['eventID' => $eventID]); 71 | } 72 | 73 | public function getCategory(): ?string 74 | { 75 | return $this->category; 76 | } 77 | 78 | public function start(Carbon $start): static 79 | { 80 | $this->start = $start->setHour(0)->setMinute(0)->setSeconds(0)->timestamp; 81 | 82 | return $this; 83 | } 84 | 85 | public function startTime(?string $startTime):static 86 | { 87 | $this->startTime = $startTime; 88 | 89 | return $this; 90 | } 91 | 92 | public function getStartTime(): ?string 93 | { 94 | return $this->startTime; 95 | } 96 | 97 | public function subject(string $subject): static 98 | { 99 | $this->subject = $subject; 100 | 101 | return $this; 102 | } 103 | 104 | public function organizer(string $organizer) 105 | { 106 | $this->organizer = $organizer; 107 | 108 | return $this; 109 | } 110 | 111 | public function participants(?array $participants) 112 | { 113 | $this->participants = $participants; 114 | 115 | return $this; 116 | } 117 | 118 | public function getColor(): ?string 119 | { 120 | return isset($this->color) ? $this->color : 'primary'; 121 | } 122 | 123 | public function getSubject(): string 124 | { 125 | return $this->subject; 126 | } 127 | 128 | public function getBody(): ?string 129 | { 130 | return $this->body; 131 | } 132 | 133 | public function getOrganizer() 134 | { 135 | return $this->organizer; 136 | } 137 | 138 | public function getStart() 139 | { 140 | return $this->start; 141 | } 142 | 143 | public function getEnd(): Carbon 144 | { 145 | return $this->end; 146 | } 147 | 148 | public function getEventID() 149 | { 150 | return $this->eventID; 151 | } 152 | 153 | public function icon(?string $icon): static 154 | { 155 | $this->icon = $icon; 156 | 157 | return $this; 158 | } 159 | 160 | public function isAllDay(bool $isAllDay): static 161 | { 162 | $this->isAllDay = $isAllDay; 163 | 164 | return $this; 165 | 166 | } 167 | 168 | public function getIcon(): ?string 169 | { 170 | return $this->icon; 171 | } 172 | 173 | public function getIsAllDay(): bool 174 | { 175 | return $this->isAllDay; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/Commands/MakeAttachmentsTableCommand.php: -------------------------------------------------------------------------------- 1 | files = $files; 34 | } 35 | 36 | public function handle(): int 37 | { 38 | $this->table = $this->ask('Please enter your event table name (e.g. `timex_events`',config('timex.tables.event.name')); 39 | $path = $this->getSourceFilePath(); 40 | $this->makeDirectory(dirname($path)); 41 | 42 | $contents = $this->getSourceFile(); 43 | 44 | if (!$this->files->exists($path)) { 45 | $this->files->put($path, $contents); 46 | $this->info("File : {$path} created"); 47 | } else { 48 | $this->info("File : {$path} already exits"); 49 | } 50 | $runMigration = $this->confirm('Would you like to run migrations?', true); 51 | if ($runMigration){ 52 | $this->call('migrate'); 53 | } 54 | 55 | return static::SUCCESS; 56 | } 57 | 58 | /** 59 | * Return the Singular Capitalize Name 60 | * @param $name 61 | * @return string 62 | */ 63 | public function getSingularClassName($name) 64 | { 65 | return ucwords(Pluralizer::singular($name)); 66 | } 67 | 68 | /** 69 | * Return the stub file path 70 | * @return string 71 | * 72 | */ 73 | public function getStubPath() 74 | { 75 | return __DIR__ . '/stubs/AttTable.stub'; 76 | } 77 | 78 | /** 79 | ** 80 | * Map the stub variables present in stub to its value 81 | * 82 | * @return array 83 | * 84 | */ 85 | public function getStubVariables() 86 | { 87 | return [ 88 | 'TABLE' => $this->table 89 | ]; 90 | } 91 | 92 | /** 93 | * Get the stub path and the stub variables 94 | * 95 | * @return bool|mixed|string 96 | * 97 | */ 98 | public function getSourceFile() 99 | { 100 | return $this->getStubContents($this->getStubPath(), $this->getStubVariables()); 101 | } 102 | 103 | /** 104 | * Replace the stub variables(key) with the desire value 105 | * 106 | * @param $stub 107 | * @param array $stubVariables 108 | * @return bool|mixed|string 109 | */ 110 | public function getStubContents($stub , $stubVariables = []) 111 | { 112 | $contents = file_get_contents($stub); 113 | 114 | foreach ($stubVariables as $search => $replace) 115 | { 116 | $contents = str_replace('$'.$search.'$' , $replace, $contents); 117 | } 118 | 119 | return $contents; 120 | 121 | } 122 | 123 | /** 124 | * Get the full path of generate class 125 | * 126 | * @return string 127 | */ 128 | public function getSourceFilePath() 129 | { 130 | return base_path('database/migrations') .'/' .now()->format('Y_m_d_hms').'_alter_'.strtolower($this->table) . '_table.php'; 131 | } 132 | 133 | /** 134 | * Build the directory for the class if necessary. 135 | * 136 | * @param string $path 137 | * @return string 138 | */ 139 | protected function makeDirectory($path) 140 | { 141 | if (! $this->files->isDirectory($path)) { 142 | $this->files->makeDirectory($path, 0777, true, true); 143 | } 144 | 145 | return $path; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /resources/views/event/view.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | $event = new \Illuminate\Support\Fluent($data); 3 | $startTime = \Carbon\Carbon::create($event->startTime); 4 | $endTime = \Carbon\Carbon::create($event->endTime); 5 | $start = \Carbon\Carbon::create($event->start)->setTimeFrom($startTime); 6 | $end = \Carbon\Carbon::create($event->end)->setTimeFrom($endTime); 7 | if (self::isCategoryModelEnabled() || \Str::isUuid($event->category)){ 8 | $model = self::getCategoryModel()::query()->find($event->category)->getAttributes(); 9 | $color = $model[self::getCategoryModelColumn('color')]; 10 | $name = $model[self::getCategoryModelColumn('value')]; 11 | }else{ 12 | $color = $event->category ? config('timex.categories.colors.'.$event->category) : 'primary'; 13 | $name = config('timex.categories.labels.'.$event->category); 14 | } 15 | $participants = json_decode($event?->participants); 16 | $attachments = json_decode($event?->attachments); 17 | 18 | if ($start == $end){ 19 | $duration = $endTime->shortRelativeDiffForHumans($startTime); 20 | }else{ 21 | $duration = $end->shortAbsoluteDiffForHumans($start,3); 22 | } 23 | @endphp 24 |
25 |
26 | 29 |
30 | {{$start->isoFormat('dddd, D MMM Y')}} 31 |
32 |
33 | {{$startTime->isoFormat('H:mm')}} 34 |
35 |
36 | 39 |
40 |
41 | @if($event->start !== $event->end) 42 | {{$end->isoFormat('H:mm, D.M.Y')}} 43 | @else 44 | {{$endTime->isoFormat('H:mm')}} 45 | @endif 46 |
47 |
48 | ({{$duration}}) 49 |
50 |
51 |
52 |
53 | @unless(!$event->category && !self::isCategoryModelEnabled()) 54 |
55 |
56 | 59 |
60 |
$color !== 'secondary', 62 | 'bg-gray-500/10 text-gray-500' => $color == 'secondary', 63 | 'rounded-xl', 64 | 'pl-2 pr-2', 65 | ])> 66 | {{$name}} 67 |
68 |
69 | @endunless 70 | @unless(!$event->body) 71 |
72 |
73 | 76 |
77 |
78 | {{\Livewire\str($event->body)->toHtmlString()}} 79 |
80 |
81 | @endunless 82 | @unless(!$participants) 83 |
84 |
85 | 88 |
89 |
90 | @foreach($participants as $participant) 91 | @php 92 | $user = \App\Models\User::find($participant) 93 | @endphp 94 |
95 |
config('filament.dark_mode'), 98 | ]) 99 | style="background-image: url('{{ \Filament\Facades\Filament::getUserAvatarUrl($user) }}')"> 100 |
101 |
102 | {{$user->name}} 103 |
104 |
105 | @endforeach 106 |
107 |
108 | @endunless 109 | @unless(!$attachments) 110 | 111 |
112 | @foreach($attachments as $attachment) 113 |
114 |
115 |
116 | 119 |
120 |
121 | {{$attachment}} 122 |
123 |
124 |
125 | 129 |
130 |
131 | @endforeach 132 |
133 | @endunless 134 |
135 | -------------------------------------------------------------------------------- /resources/svg/timex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Calendar/Month.php: -------------------------------------------------------------------------------- 1 | 'onEventChanged', 26 | 'modelUpdated' => 'loaded', 27 | 'onTodayClick' => 'onTodayClick', 28 | 'onPrevClick' => 'onPreviousMonthClick', 29 | 'onNextClick' => 'onNextMonthClick', 30 | 'onNextYearClick' => 'onNextYearClick', 31 | 'onPreviousYearClick' => 'onPreviousYearClick', 32 | 'onMonthDropDownClick' => 'onMonthDropDownClick', 33 | ]; 34 | 35 | 36 | protected function setCalendar(): void 37 | { 38 | $this->setStartOfMonth(); 39 | $this->setEndOfMonth(); 40 | 41 | $this->currMonth = collect([]); 42 | while ($this->getStartOfMonth() <= $this->getEndOfMonth()){ 43 | $this->currMonth->push([ 44 | 'id' => $this->startOfMonth->timestamp, 45 | 'group' => "{$this->id}-{$this->startOfMonth->dayOfWeek}", 46 | 'dayOfWeek' => $this->startOfMonth->dayOfWeek, 47 | 'day' => $this->startOfMonth->day, 48 | 'timestamp' => $this->startOfMonth->timestamp, 49 | ]); 50 | $this->startOfMonth->addDay(); 51 | } 52 | 53 | $this->setDayLabels(); 54 | 55 | } 56 | 57 | public function setDayLabels(): void 58 | { 59 | $this->dayLabels = collect([]); 60 | $dayOne = 0; 61 | foreach($this->currMonth as $weekOfDay ){ 62 | $this->dayLabels->push([ 63 | 'dayOfWeek' => Carbon::createFromTimestamp($weekOfDay['id'])->dayOfWeek, 64 | 'dayName' => match (config('timex.dayName')){ 65 | 'dayName' => Carbon::createFromTimestamp($weekOfDay['id'])->dayName, 66 | 'shortDayName' => Carbon::createFromTimestamp($weekOfDay['id'])->shortDayName, 67 | default => Carbon::createFromTimestamp($weekOfDay['id'])->minDayName, 68 | 69 | } 70 | ]); 71 | if(++$dayOne > 6) break; 72 | } 73 | } 74 | 75 | public function getDays(): array 76 | { 77 | $weekDays = $this->dayLabels; 78 | $currMonthDays = $this->currMonth; 79 | $weekDays = $weekDays 80 | ->map(function ($month) use ($currMonthDays) { 81 | $month['group'] = uuid_create(); 82 | $month['dayName'] = $month['dayName']; 83 | $month['days'] = $currMonthDays 84 | ->filter(function ($currMonthDays) use ($month) { 85 | return $this->dayInWeekDay($currMonthDays, $month); 86 | }); 87 | return $month; 88 | }); 89 | return [ 90 | 'weekDays' => $weekDays, 91 | 'fullDays' => $currMonthDays 92 | ]; 93 | } 94 | 95 | 96 | public function boot() 97 | { 98 | $this->setCalendar(); 99 | } 100 | 101 | public function mount() 102 | { 103 | $this->monthName = $this->getMonthName(today()); 104 | } 105 | 106 | public function render() 107 | { 108 | return view('timex::calendar.month'); 109 | } 110 | 111 | public function onPreviousMonthClick() 112 | { 113 | $this->today = $this->today->subMonth(); 114 | $this->monthName = $this->getMonthName($this->today); 115 | $this->setCalendar(); 116 | $this->loaded(); 117 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 118 | } 119 | 120 | public function onNextMonthClick() 121 | { 122 | $this->today = $this->today->addMonth(); 123 | $this->monthName = $this->getMonthName($this->today); 124 | $this->setCalendar(); 125 | $this->loaded(); 126 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 127 | } 128 | 129 | public function onNextYearClick() 130 | { 131 | $this->today = $this->today->addYear(); 132 | $this->monthName = $this->getMonthName($this->today); 133 | $this->setCalendar(); 134 | $this->loaded(); 135 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 136 | } 137 | 138 | public function onPreviousYearClick() 139 | { 140 | $this->today = $this->today->subYear(); 141 | $this->monthName = $this->getMonthName($this->today); 142 | $this->setCalendar(); 143 | $this->loaded(); 144 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 145 | } 146 | 147 | public function onTodayClick() 148 | { 149 | $this->today = Carbon::today(); 150 | $this->monthName = $this->getMonthName($this->today); 151 | $this->setCalendar(); 152 | $this->loaded(); 153 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 154 | } 155 | 156 | public function onMonthDropDownClick($month) 157 | { 158 | $this->today = Carbon::createFromTimestamp($month); 159 | $this->monthName = $this->getMonthName($this->today); 160 | $this->setCalendar(); 161 | $this->loaded(); 162 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 163 | } 164 | 165 | public function setStartOfMonth() 166 | { 167 | $this->startOfMonth = Carbon::create($this->today)->firstOfMonth()->startOfWeek($this->getStartOfWeek()); 168 | } 169 | 170 | public function setEndOfMonth() 171 | { 172 | $this->endOfMonth = Carbon::create($this->today)->lastOfMonth()->endOfWeek($this->getEndOfWeek()); 173 | } 174 | 175 | 176 | public function getStartOfMonth(): Carbon 177 | { 178 | return $this->startOfMonth; 179 | } 180 | 181 | public function getEndOfMonth(): Carbon 182 | { 183 | return $this->endOfMonth; 184 | } 185 | 186 | protected function dayInWeekDay($day, $weekDay) 187 | { 188 | return $day['dayOfWeek'] === $weekDay['dayOfWeek']; 189 | } 190 | 191 | public function loaded(){ 192 | 193 | $this->dispatchBrowserEvent('monthLoaded',$this->getDays()); 194 | $this->emitUp('monthNameChanged',$this->today,$this->today->year); 195 | } 196 | public function onEventChanged($eventID, $toDate) 197 | { 198 | $this->emitUp('eventUpdated',['id' => $eventID,'toDate' => $toDate]); 199 | $this->shouldSkipRender = true; 200 | } 201 | 202 | public function getMonthName($date){ 203 | 204 | return $date->monthName.' '.$date->year; 205 | 206 | } 207 | 208 | 209 | 210 | 211 | } 212 | -------------------------------------------------------------------------------- /resources/views/header/header.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 | 9 |
10 |
11 |
12 | {{$this->monthName}} 13 |
14 | 18 |
19 |
20 |
21 | 22 | 23 |
24 | 29 |
30 |
31 | 35 |
36 |
37 | {{$this->getYearFormat($this->year)}} 38 |
39 |
40 | 44 |
45 |
46 |
47 |
48 |
52 | @foreach($this->period as $month) 53 | @php 54 | $color = \Carbon\Carbon::create($month)->isCurrentMonth() ? 'primary' : 'secondary'; 55 | @endphp 56 | 63 | {{$month->shortMonthName}} 64 | 65 | @endforeach 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 80 |
81 | {{config('timex.pages.buttons.today.static') ? trans('timex::timex.labels.today') : self::getDynamicLabel('today')}} 82 |
83 |
84 |
85 |
86 | 90 |
91 | @unless(config('timex.pages.buttons.hideYearNavigation', false)) 92 | 97 | @endunless 98 | 103 | 106 | 107 | 112 | @unless(config('timex.pages.buttons.hideYearNavigation', false)) 113 | 118 | @endunless 119 |
120 |
121 | 126 |
127 | 131 | 135 |
136 |
137 |
138 |
139 |
140 | -------------------------------------------------------------------------------- /config/timex.php: -------------------------------------------------------------------------------- 1 | 'timex', 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | TIMEX Mini widget 20 | |-------------------------------------------------------------------------- 21 | | 22 | | You can disable or enable individually widgets or entirely the whole view. 23 | | 24 | */ 25 | 26 | 'mini' => [ 27 | 'isMiniCalendarEnabled' => true, 28 | 'isDayViewHidden' => false, 29 | 'isNextMeetingViewHidden' => false, 30 | ], 31 | 32 | /* 33 | |-------------------------------------------------------------------------- 34 | | TIMEX Calendar configurations 35 | |-------------------------------------------------------------------------- 36 | | 37 | | Change according to your locale. 38 | | 39 | */ 40 | 41 | 'week' => [ 42 | 'start' => Carbon::MONDAY, 43 | 'end' => Carbon::SUNDAY 44 | ], 45 | 'isDayClickEnabled' => true, 46 | 47 | 'dayName' => 'minDayName', // minDayName or dayName or shortDayName 48 | 49 | 'dropDownCols' => 3, 50 | 51 | 'isPastCreationEnabled' => false, 52 | 53 | /* 54 | |-------------------------------------------------------------------------- 55 | | TIMEX Resources & Pages 56 | |-------------------------------------------------------------------------- 57 | | 58 | | By default TIMEX out of box will work, just make sure you make migration. 59 | | But you can also make your own Model and Filament resource and update config accordingly 60 | | 61 | */ 62 | 63 | 'pages' => [ 64 | 'timex' => \Buildix\Timex\Pages\Timex::class, 65 | 'slug' => 'timex', 66 | 'group' => 'timex', 67 | 'sort' => 0, 68 | 'shouldRegisterNavigation' => true, 69 | 'enablePolicy' => false, 70 | 'modalWidth' => 'xl', 71 | 'icon' => [ 72 | 'static' => true, 73 | 'timex' => 'timex-timex', 74 | 'day' => 'timex-day-' 75 | ], 76 | 'label' => [ 77 | 'navigation' => [ 78 | 'static' => false, 79 | 'format' => 'dddd, D MMM', 80 | ], 81 | 'breadcrumbs' => [ 82 | 'static' => false, 83 | 'format' => 'dddd, D MMM', 84 | ], 85 | 'title' => [ 86 | 'static' => false, 87 | 'format' => 'dddd, D MMM', 88 | ], 89 | ], 90 | 'buttons' => [ 91 | 'hideYearNavigation' => false, 92 | 'today' => [ 93 | 'static' => false, 94 | 'format' => 'D MMM' 95 | ], 96 | 'outlined' => true, 97 | 'icons' => [ 98 | 'previousYear' => 'heroicon-o-chevron-double-left', 99 | 'nextYear' => 'heroicon-o-chevron-double-right', 100 | 'previousMonth' => 'heroicon-o-chevron-left', 101 | 'nextMonth' => 'heroicon-o-chevron-right', 102 | 'createEvent' => 'heroicon-o-plus' 103 | ], 104 | 'modal' => [ 105 | 'submit' => [ 106 | 'outlined' => false, 107 | 'color' => 'primary', 108 | 'icon' => [ 109 | 'enabled' => true, 110 | 'name' => 'heroicon-o-save' 111 | ], 112 | ], 113 | 'cancel' => [ 114 | 'outlined' => false, 115 | 'color' => 'secondary', 116 | 'icon' => [ 117 | 'enabled' => true, 118 | 'name' => 'heroicon-o-x-circle' 119 | ], 120 | ], 121 | 'delete' => [ 122 | 'outlined' => false, 123 | 'color' => 'danger', 124 | 'icon' => [ 125 | 'enabled' => true, 126 | 'name' => 'heroicon-o-trash' 127 | ], 128 | ], 129 | 'edit' => [ 130 | 'outlined' => false, 131 | 'color' => 'primary', 132 | 'icon' => [ 133 | 'enabled' => true, 134 | 'name' => 'heroicon-o-pencil-alt' 135 | ], 136 | ], 137 | 'view' => [ 138 | 'time' => 'heroicon-o-clock', 139 | 'category' => 'heroicon-o-tag', 140 | 'body' => 'heroicon-o-annotation', 141 | 'participants' => 'heroicon-o-user-group', 142 | ], 143 | ], 144 | ], 145 | ], 146 | 147 | 'resources' => [ 148 | 'event' => \Buildix\Timex\Resources\EventResource::class, 149 | 'sort' => 1, 150 | 'icon' => 'heroicon-o-calendar', 151 | 'slug' => 'timex-events', 152 | 'shouldRegisterNavigation' => true, 153 | 'isStartEndHidden' => false, 154 | ], 155 | 'models' => [ 156 | 'event' => \Buildix\Timex\Models\Event::class, 157 | 'users' => [ 158 | 'model' => \App\Models\User::class, 159 | 'name' => 'name', 160 | 'id' => 'id', 161 | ], 162 | ], 163 | 'tables' => [ 164 | 'event' => [ 165 | 'name' => 'timex_events', 166 | ], 167 | 'category' => [ 168 | 'name' => 'timex_categories', 169 | ], 170 | ], 171 | 172 | /* 173 | |-------------------------------------------------------------------------- 174 | | TIMEX Event categories 175 | |-------------------------------------------------------------------------- 176 | | 177 | | Categories names are used to define colors & icons. 178 | | Each represents default tailwind colors. 179 | | You may change as you wish, just make sure your color have -500 / -600 and etc variants 180 | | You may also go for a custom Category model to define your labels, colors and icons 181 | | 182 | */ 183 | 184 | 'categories' => [ 185 | 'isModelEnabled' => false, 186 | /* 187 | |-------------------------------------------------------------------------- 188 | | Category Model 189 | |-------------------------------------------------------------------------- 190 | | 191 | | You can define your custom Category model. 192 | | Minimum and default columns in your DB should be: id, value, icon, color. 193 | | 194 | | 195 | */ 196 | 'model' => [ 197 | 'class' => \Buildix\Timex\Models\Category::class, // \App\Models\Category::class 198 | 'key' => 'id', // "id" is a DB column - you can change by any primary key 199 | 'value' => 'value', // "value" is a DB column - it used for Select options and displays on Resource page 200 | 'icon' => 'icon', // "icon" is a DB column - define here any heroicon- icon 201 | 'color' => 'color', // "color" is a DB column - default tailwindcss colors names like: primary / secondary / danger 202 | ], 203 | /* 204 | |-------------------------------------------------------------------------- 205 | | Default TiMEX Categories 206 | |-------------------------------------------------------------------------- 207 | */ 208 | 'labels' => [ 209 | 'primary' => 'Primary category', 210 | 'secondary' => 'Secondary category', 211 | 'danger' => 'Danger category', 212 | 'success' => 'Success category', 213 | ], 214 | 'icons' => [ 215 | 'primary' => 'heroicon-o-clipboard', 216 | 'secondary' => 'heroicon-o-bookmark', 217 | 'danger' => 'heroicon-o-flag', 218 | 'success' => 'heroicon-o-badge-check', 219 | ], 220 | 'colors' => [ 221 | 'primary' => 'primary', 222 | 'secondary' => 'secondary', 223 | 'danger' => 'danger', 224 | 'success' => 'success', 225 | ], 226 | ], 227 | 228 | ]; 229 | -------------------------------------------------------------------------------- /configure.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | $version) { 90 | if (in_array($name, $names, true)) { 91 | unset($data['require-dev'][$name]); 92 | } 93 | } 94 | 95 | file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); 96 | } 97 | 98 | function remove_composer_script($scriptName) 99 | { 100 | $data = json_decode(file_get_contents(__DIR__.'/composer.json'), true); 101 | 102 | foreach ($data['scripts'] as $name => $script) { 103 | if ($scriptName === $name) { 104 | unset($data['scripts'][$name]); 105 | break; 106 | } 107 | } 108 | 109 | file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); 110 | } 111 | 112 | function remove_readme_paragraphs(string $file): void 113 | { 114 | $contents = file_get_contents($file); 115 | 116 | file_put_contents( 117 | $file, 118 | preg_replace('/.*/s', '', $contents) ?: $contents 119 | ); 120 | } 121 | 122 | function safeUnlink(string $filename) 123 | { 124 | if (file_exists($filename) && is_file($filename)) { 125 | unlink($filename); 126 | } 127 | } 128 | 129 | function determineSeparator(string $path): string 130 | { 131 | return str_replace('/', DIRECTORY_SEPARATOR, $path); 132 | } 133 | 134 | function replaceForWindows(): array 135 | { 136 | return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .git\ | findstr /v /i vendor | findstr /v /i '.basename(__FILE__).' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton migration_table_name vendor_name vendor_slug author@domain.com"')); 137 | } 138 | 139 | function replaceForAllOtherOSes(): array 140 | { 141 | return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|migration_table_name|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v '.basename(__FILE__))); 142 | } 143 | 144 | $gitName = run('git config user.name'); 145 | $authorName = ask('Author name', $gitName); 146 | 147 | $gitEmail = run('git config user.email'); 148 | $authorEmail = ask('Author email', $gitEmail); 149 | 150 | $usernameGuess = explode(':', run('git config remote.origin.url'))[1]; 151 | $usernameGuess = dirname($usernameGuess); 152 | $usernameGuess = basename($usernameGuess); 153 | $authorUsername = ask('Author username', $usernameGuess); 154 | 155 | $vendorName = ask('Vendor name', $authorUsername); 156 | $vendorSlug = slugify($vendorName); 157 | $vendorNamespace = str_replace('-', '', ucwords($vendorName)); 158 | $vendorNamespace = ask('Vendor namespace', $vendorNamespace); 159 | 160 | $currentDirectory = getcwd(); 161 | $folderName = basename($currentDirectory); 162 | 163 | $packageName = ask('Package name', $folderName); 164 | $packageSlug = slugify($packageName); 165 | $packageSlugWithoutPrefix = remove_prefix('laravel-', $packageSlug); 166 | 167 | $className = title_case($packageName); 168 | $className = ask('Class name', $className); 169 | $variableName = lcfirst($className); 170 | $description = ask('Package description', "This is my package {$packageSlug}"); 171 | 172 | $usePhpStan = confirm('Enable PhpStan?', true); 173 | $useLaravelPint = confirm('Enable Laravel Pint?', true); 174 | $useDependabot = confirm('Enable Dependabot?', true); 175 | $useLaravelRay = confirm('Use Ray for debugging?', true); 176 | $useUpdateChangelogWorkflow = confirm('Use automatic changelog updater workflow?', true); 177 | 178 | writeln('------'); 179 | writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})"); 180 | writeln("Vendor : {$vendorName} ({$vendorSlug})"); 181 | writeln("Package : {$packageSlug} <{$description}>"); 182 | writeln("Namespace : {$vendorNamespace}\\{$className}"); 183 | writeln("Class name : {$className}"); 184 | writeln('---'); 185 | writeln('Packages & Utilities'); 186 | writeln('Use Laravel/Pint : '.($useLaravelPint ? 'yes' : 'no')); 187 | writeln('Use Larastan/PhpStan : '.($usePhpStan ? 'yes' : 'no')); 188 | writeln('Use Dependabot : '.($useDependabot ? 'yes' : 'no')); 189 | writeln('Use Ray App : '.($useLaravelRay ? 'yes' : 'no')); 190 | writeln('Use Auto-Changelog : '.($useUpdateChangelogWorkflow ? 'yes' : 'no')); 191 | writeln('------'); 192 | 193 | writeln('This script will replace the above values in all relevant files in the project directory.'); 194 | 195 | if (! confirm('Modify files?', true)) { 196 | exit(1); 197 | } 198 | 199 | $files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes()); 200 | 201 | foreach ($files as $file) { 202 | replace_in_file($file, [ 203 | ':author_name' => $authorName, 204 | ':author_username' => $authorUsername, 205 | 'author@domain.com' => $authorEmail, 206 | ':vendor_name' => $vendorName, 207 | ':vendor_slug' => $vendorSlug, 208 | 'VendorName' => $vendorNamespace, 209 | ':package_name' => $packageName, 210 | ':package_slug' => $packageSlug, 211 | ':package_slug_without_prefix' => $packageSlugWithoutPrefix, 212 | 'Skeleton' => $className, 213 | 'skeleton' => $packageSlug, 214 | 'migration_table_name' => title_snake($packageSlug), 215 | 'variable' => $variableName, 216 | ':package_description' => $description, 217 | ]); 218 | 219 | match (true) { 220 | str_contains($file, determineSeparator('src/Skeleton.php')) => rename($file, determineSeparator('./src/'.$className.'.php')), 221 | str_contains($file, determineSeparator('src/SkeletonServiceProvider.php')) => rename($file, determineSeparator('./src/'.$className.'ServiceProvider.php')), 222 | str_contains($file, determineSeparator('src/Facades/Skeleton.php')) => rename($file, determineSeparator('./src/Facades/'.$className.'.php')), 223 | str_contains($file, determineSeparator('src/Commands/SkeletonCommand.php')) => rename($file, determineSeparator('./src/Commands/'.$className.'Command.php')), 224 | str_contains($file, determineSeparator('database/migrations/create_skeleton_table.php.stub')) => rename($file, determineSeparator('./database/migrations/create_'.title_snake($packageSlugWithoutPrefix).'_table.php.stub')), 225 | str_contains($file, determineSeparator('config/skeleton.php')) => rename($file, determineSeparator('./config/'.$packageSlugWithoutPrefix.'.php')), 226 | str_contains($file, 'README.md') => remove_readme_paragraphs($file), 227 | default => [], 228 | }; 229 | } 230 | 231 | if (! $useLaravelPint) { 232 | safeUnlink(__DIR__.'/.github/workflows/fix-php-code-style-issues.yml'); 233 | safeUnlink(__DIR__.'/pint.json'); 234 | } 235 | 236 | if (! $usePhpStan) { 237 | safeUnlink(__DIR__.'/phpstan.neon.dist'); 238 | safeUnlink(__DIR__.'/phpstan-baseline.neon'); 239 | safeUnlink(__DIR__.'/.github/workflows/phpstan.yml'); 240 | 241 | remove_composer_deps([ 242 | 'phpstan/extension-installer', 243 | 'phpstan/phpstan-deprecation-rules', 244 | 'phpstan/phpstan-phpunit', 245 | 'nunomaduro/larastan', 246 | ]); 247 | 248 | remove_composer_script('phpstan'); 249 | } 250 | 251 | if (! $useDependabot) { 252 | safeUnlink(__DIR__.'/.github/dependabot.yml'); 253 | safeUnlink(__DIR__.'/.github/workflows/dependabot-auto-merge.yml'); 254 | } 255 | 256 | if (! $useLaravelRay) { 257 | remove_composer_deps(['spatie/laravel-ray']); 258 | } 259 | 260 | if (! $useUpdateChangelogWorkflow) { 261 | safeUnlink(__DIR__.'/.github/workflows/update-changelog.yml'); 262 | } 263 | 264 | confirm('Execute `composer install` and run tests?') && run('composer install && composer test'); 265 | 266 | confirm('Let this script delete itself?', true) && unlink(__FILE__); 267 | -------------------------------------------------------------------------------- /src/Pages/Timex.php: -------------------------------------------------------------------------------- 1 | day; 87 | } 88 | 89 | public static function getSlug(): string 90 | { 91 | return config('timex.pages.slug'); 92 | } 93 | 94 | protected static function shouldRegisterNavigation(): bool 95 | { 96 | if (!config('timex.pages.shouldRegisterNavigation')){ 97 | return false; 98 | } 99 | if (config('timex.pages.enablePolicy',false) && \Gate::getPolicyFor(self::getModel()) && !\Gate::allows('viewAny',self::getModel())){ 100 | return false; 101 | } 102 | 103 | return true; 104 | } 105 | 106 | protected function getHeading(): string|Htmlable 107 | { 108 | return " "; 109 | } 110 | 111 | public function monthNameChanged($data,$year) 112 | { 113 | $this->monthName = Carbon::create($data)->monthName.' '.$this->getYearFormat($data); 114 | $this->year = Carbon::create($data); 115 | $this->period = CarbonPeriod::create(Carbon::create($data)->firstOfYear(),'1 month',Carbon::create($data)->lastOfYear()); 116 | } 117 | 118 | public function __construct() 119 | { 120 | $this->monthName = today()->monthName." ".today()->year; 121 | $this->year = today(); 122 | $this->period = CarbonPeriod::create(Carbon::create($this->year->firstOfYear()),'1 month',$this->year->lastOfYear()); 123 | 124 | } 125 | 126 | protected function getActions(): array 127 | { 128 | return [ 129 | Action::make('openCreateModal') 130 | ->label(trans('filament::resources/pages/create-record.title', 131 | ['label' => Str::lower(__('timex::timex.model.label'))])) 132 | ->icon(config('timex.pages.buttons.icons.createEvent')) 133 | ->size('sm') 134 | ->outlined(config('timex.pages.buttons.outlined')) 135 | ->slideOver() 136 | ->extraAttributes(['class' => '-mr-2']) 137 | ->form($this->getResourceForm(2)->getSchema()) 138 | ->modalHeading(trans('timex::timex.model.label')) 139 | ->modalWidth(config('timex.pages.modalWidth')) 140 | ->action(fn(array $data) => $this->updateOrCreate($data)) 141 | ->modalActions([ 142 | Action::makeModalAction('submit') 143 | ->label(trans('timex::timex.modal.submit')) 144 | ->color(config('timex.pages.buttons.modal.submit.color','primary')) 145 | ->outlined(config('timex.pages.buttons.modal.submit.outlined',false)) 146 | ->icon(config('timex.pages.buttons.modal.submit.icon.name','')) 147 | ->submit(), 148 | Action::makeModalAction('delete') 149 | ->label(trans('timex::timex.modal.delete')) 150 | ->color(config('timex.pages.buttons.modal.delete.color','danger')) 151 | ->outlined(config('timex.pages.buttons.modal.delete.outlined', false)) 152 | ->icon(config('timex.pages.buttons.modal.delete.icon.name','')) 153 | ->action('deleteEvent') 154 | ->cancel(), 155 | Action::makeModalAction('cancel') 156 | ->label(trans('timex::timex.modal.cancel')) 157 | ->color(config('timex.pages.buttons.modal.cancel.color','secondary')) 158 | ->outlined(config('timex.pages.buttons.modal.cancel.outlined',false)) 159 | ->icon(config('timex.pages.buttons.modal.cancel.icon.name','')) 160 | ->cancel(), 161 | ]), 162 | ]; 163 | } 164 | 165 | public static function getEvents(): array 166 | { 167 | $events = self::getModel()::orderBy('startTime')->get() 168 | ->map(function ($event){ 169 | return EventItem::make($event->id) 170 | ->body($event->body) 171 | ->category($event->category) 172 | ->color($event->category) 173 | ->end(Carbon::create($event->end)) 174 | ->isAllDay($event->isAllDay) 175 | ->subject($event->subject) 176 | ->organizer($event->organizer) 177 | ->participants($event?->participants) 178 | ->start(Carbon::create($event->start)) 179 | ->startTime($event?->startTime); 180 | })->toArray(); 181 | 182 | return collect($events)->filter(function ($event){ 183 | return $event->organizer == \Auth::id() || in_array(\Auth::id(), $event?->participants ?? []); 184 | })->toArray(); 185 | } 186 | 187 | public function updateOrCreate($data) 188 | { 189 | if ($data['organizer'] == null){ 190 | $this->getModel()::query()->create([...$data,'organizer' => \Auth::id()]); 191 | }else{ 192 | $this->getFormModel()::query()->find($this->getFormModel()->id)->update($data); 193 | } 194 | $this->dispatEventUpdates(); 195 | } 196 | 197 | public function deleteEvent() 198 | { 199 | $this->getFormModel()->delete(); 200 | $this->dispatEventUpdates(); 201 | } 202 | 203 | public function dispatEventUpdates(): void 204 | { 205 | $this->emit('modelUpdated',['id' => $this->id]); 206 | $this->emit('updateWidget',['id' => $this->id]); 207 | } 208 | 209 | public function onEventClick($eventID) 210 | { 211 | $this->record = $eventID; 212 | $event = $this->getFormModel()->getAttributes(); 213 | $this->mountAction('openCreateModal'); 214 | 215 | if ($this->getFormModel()->getAttribute('organizer') !== \Auth::id()){ 216 | $this->getMountedAction() 217 | ->modalContent(\view('timex::event.view',['data' => $event])) 218 | ->modalHeading($event['subject']) 219 | ->form([]) 220 | ->modalActions([ 221 | 222 | ]); 223 | }else{ 224 | $this->getMountedActionForm() 225 | ->fill([ 226 | ...$event, 227 | 'participants' => self::getFormModel()?->participants, 228 | 'attachments' => self::getFormModel()?->attachments, 229 | ]); 230 | } 231 | } 232 | 233 | public function onDayClick($timestamp) 234 | { 235 | if (config('timex.isDayClickEnabled',true)){ 236 | if (config('timex.isPastCreationEnabled',false)){ 237 | $this->onCreateClick($timestamp); 238 | }else{ 239 | Carbon::createFromTimestamp($timestamp)->isBefore(Carbon::today()) ? '' : $this->onCreateClick($timestamp); 240 | } 241 | } 242 | } 243 | 244 | public function onCreateClick(int | string | null $timestamp = null) 245 | { 246 | $this->mountAction('openCreateModal'); 247 | $this->getMountedActionForm() 248 | ->fill([ 249 | 'startTime' => Carbon::now()->setMinutes(0)->addHour(), 250 | 'endTime' => Carbon::now()->setMinutes(0)->addHour()->addMinutes(30), 251 | 'start' => Carbon::createFromTimestamp(isset($timestamp) ? $timestamp : today()->timestamp), 252 | 'end' => Carbon::createFromTimestamp(isset($timestamp) ? $timestamp : today()->timestamp) 253 | ]); 254 | } 255 | 256 | protected function getHeader(): ?View 257 | { 258 | return \view('timex::header.header'); 259 | } 260 | 261 | public function onNextDropDownYearClick() 262 | { 263 | $this->year = $this->year->addYear(); 264 | $this->period = CarbonPeriod::create(Carbon::create($this->year->firstOfYear()),'1 month',$this->year->lastOfYear()); 265 | } 266 | 267 | public function onPrevDropDownYearClick() 268 | { 269 | $this->year = $this->year->subYear(); 270 | $this->period = CarbonPeriod::create(Carbon::create($this->year->firstOfYear()),'1 month',$this->year->lastOfYear()); 271 | } 272 | 273 | 274 | public function getYearFormat($data) 275 | { 276 | return Carbon::create($data)->year; 277 | } 278 | 279 | public function loadAttachment($file): void 280 | { 281 | $this->redirect(Storage::url($file)); 282 | } 283 | 284 | } 285 | -------------------------------------------------------------------------------- /src/Resources/EventResource.php: -------------------------------------------------------------------------------- 1 | schema([ 92 | Hidden::make('organizer'), 93 | TextInput::make('subject') 94 | ->label(trans('timex::timex.event.subject')) 95 | ->required() 96 | ->columnSpanFull(), 97 | RichEditor::make('body') 98 | ->label(trans('timex::timex.event.body')) 99 | ->columnSpanFull(), 100 | Select::make('participants') 101 | ->label(trans('timex::timex.event.participants')) 102 | ->options(function (){ 103 | return self::getUserModel()::all() 104 | ->pluck(self::getUserModelColumn('name'),self::getUserModelColumn('id')); 105 | }) 106 | ->multiple()->columnSpanFull()->hidden(!in_array('participants',\Schema::getColumnListing(self::getEventTableName()))), 107 | Select::make('category') 108 | ->label(trans('timex::timex.event.category')) 109 | ->columnSpanFull() 110 | ->searchable() 111 | ->preload() 112 | ->options(function (){ 113 | return self::isCategoryModelEnabled() ? self::getCategoryModel()::all() 114 | ->pluck(self::getCategoryModelColumn('value'),self::getCategoryModelColumn('key')) 115 | : config('timex.categories.labels'); 116 | }) 117 | ->columnSpanFull(), 118 | Grid::make(3)->schema([ 119 | Toggle::make('isAllDay') 120 | ->label(trans('timex::timex.event.allDay')) 121 | ->columnSpanFull() 122 | ->reactive() 123 | ->afterStateUpdated(function ($set, callable $get, $state){ 124 | $start = today()->setHours(0)->setMinutes(0); 125 | $end = today()->setHours(23)->setMinutes(59); 126 | if ($state == true){ 127 | $set('startTime',$start); 128 | $set('endTime',$end); 129 | }else{ 130 | $set('startTime',now()->setMinutes(0)->addHour()); 131 | $set('endTime',now()->setMinutes(0)->addHour()->addMinutes(30)); 132 | } 133 | }), 134 | DatePicker::make('start') 135 | ->label(trans('timex::timex.event.start')) 136 | ->columnSpan(function (){ 137 | return config('timex.resources.isStartEndHidden',false) ? 'full' : 2; 138 | }) 139 | ->inlineLabel() 140 | ->default(today()) 141 | ->minDate(today()) 142 | ->required() 143 | ->reactive() 144 | ->afterStateUpdated(function ($get,$set,$state){ 145 | if ($get('end') < $state){ 146 | $set('end',$state); 147 | } 148 | }) 149 | ->extraAttributes([ 150 | 'class' => '-ml-2' 151 | ]) 152 | ->firstDayOfWeek(config('timex.week.start')), 153 | TimePicker::make('startTime') 154 | ->hidden(config('timex.resources.isStartEndHidden',false)) 155 | ->withoutSeconds() 156 | ->disableLabel() 157 | ->required() 158 | ->default(now()->setMinutes(0)->addHour()) 159 | ->reactive() 160 | ->extraAttributes([ 161 | 'class' => '-ml-2' 162 | ]) 163 | ->afterStateUpdated(function ($set,$state){ 164 | $set('endTime',Carbon::parse($state)->addMinutes(30)); 165 | }) 166 | ->disabled(function ($get){ 167 | return $get('isAllDay'); 168 | }), 169 | DatePicker::make('end') 170 | ->label(trans('timex::timex.event.end')) 171 | ->inlineLabel() 172 | ->columnSpan(function (){ 173 | return config('timex.resources.isStartEndHidden',false) ? 'full' : 2; 174 | }) 175 | ->default(today()) 176 | ->minDate(today()) 177 | ->reactive() 178 | ->extraAttributes([ 179 | 'class' => '-ml-2' 180 | ]) 181 | ->firstDayOfWeek(config('timex.week.start')), 182 | TimePicker::make('endTime') 183 | ->hidden(config('timex.resources.isStartEndHidden',false)) 184 | ->withoutSeconds() 185 | ->disableLabel() 186 | ->reactive() 187 | ->extraAttributes([ 188 | 'class' => '-ml-2' 189 | ]) 190 | ->default(now()->setMinutes(0)->addHour()->addMinutes(30)) 191 | ->disabled(function ($get){ 192 | return $get('isAllDay'); 193 | }), 194 | ])->columnSpanFull(), 195 | Section::make('Attachments')->schema([ 196 | FileUpload::make('attachments') 197 | ->multiple() 198 | ->preserveFilenames() 199 | ->disablePreview() 200 | ->disableLabel() 201 | ->enableDownload() 202 | ->enableOpen() 203 | ]) 204 | ->heading(trans('timex::timex.event.attachments')) 205 | ->hidden(!in_array('attachments',\Schema::getColumnListing(self::getEventTableName()))) 206 | ->columnSpanFull() 207 | ->collapsible() 208 | ->collapsed(function ($get){ 209 | return $get('attachments') == null ? true : false; 210 | }) 211 | ->compact(), 212 | ]); 213 | } 214 | 215 | public static function getCreateEditForm(): array 216 | { 217 | return [ 218 | Grid::make(3)->schema([ 219 | Card::make([ 220 | TextInput::make('subject') 221 | ->label(trans('timex::timex.event.subject')) 222 | ->required(), 223 | RichEditor::make('body') 224 | ->label(trans('timex::timex.event.body')), 225 | ])->columnSpan(2), 226 | Card::make([ 227 | Grid::make(3)->schema([ 228 | Toggle::make('isAllDay') 229 | ->label(trans('timex::timex.event.allDay')) 230 | ->columnSpanFull() 231 | ->reactive() 232 | ->afterStateUpdated(function ($set, callable $get, $state){ 233 | $start = today()->setHours(0)->setMinutes(0); 234 | $end = today()->setHours(23)->setMinutes(59); 235 | if ($state == true){ 236 | $set('startTime',$start); 237 | $set('endTime',$end); 238 | }else{ 239 | $set('startTime',now()->setMinutes(0)->addHour()); 240 | $set('endTime',now()->setMinutes(0)->addHour()->addMinutes(30)); 241 | } 242 | }), 243 | DatePicker::make('start') 244 | ->label(trans('timex::timex.event.start')) 245 | ->inlineLabel() 246 | ->columnSpan(function (){ 247 | return config('timex.resources.isStartEndHidden',false) ? 'full' : 2; 248 | }) 249 | ->default(today()) 250 | ->minDate(today()) 251 | ->firstDayOfWeek(config('timex.week.start')), 252 | TimePicker::make('startTime') 253 | ->hidden(config('timex.resources.isStartEndHidden',false)) 254 | ->withoutSeconds() 255 | ->disableLabel() 256 | ->default(now()->setMinutes(0)->addHour()) 257 | ->reactive() 258 | ->afterStateUpdated(function ($set,$state){ 259 | $set('endTime',Carbon::parse($state)->addMinutes(30)); 260 | }) 261 | ->disabled(function ($get){ 262 | return $get('isAllDay'); 263 | }), 264 | DatePicker::make('end') 265 | ->label(trans('timex::timex.event.end')) 266 | ->inlineLabel() 267 | ->columnSpan(function (){ 268 | return config('timex.resources.isStartEndHidden',false) ? 'full' : 2; 269 | }) 270 | ->default(today()) 271 | ->minDate(today()) 272 | ->firstDayOfWeek(config('timex.week.start')), 273 | TimePicker::make('endTime') 274 | ->hidden(config('timex.resources.isStartEndHidden',false)) 275 | ->withoutSeconds() 276 | ->disableLabel() 277 | ->reactive() 278 | ->default(now()->setMinutes(0)->addHour()->addMinutes(30)) 279 | ->disabled(function ($get){ 280 | return $get('isAllDay'); 281 | }), 282 | Select::make('participants') 283 | ->label(trans('timex::timex.event.participants')) 284 | ->options(function (){ 285 | return self::getUserModel()::all() 286 | ->pluck(self::getUserModelColumn('name'),self::getUserModelColumn('id')); 287 | }) 288 | ->multiple()->columnSpanFull()->hidden(!in_array('participants',\Schema::getColumnListing(self::getEventTableName()))), 289 | Select::make('category') 290 | ->label(trans('timex::timex.event.category')) 291 | ->columnSpanFull() 292 | ->options(function (){ 293 | return self::isCategoryModelEnabled() ? self::getCategoryModel()::all() 294 | ->pluck(self::getCategoryModelColumn('value'),self::getCategoryModelColumn('key')) 295 | : config('timex.categories.labels'); 296 | }) 297 | ->createOptionForm(function (){ 298 | return self::isCategoryModelEnabled() ? [ 299 | TextInput::make('value')->required(), 300 | TextInput::make('icon'), 301 | TextInput::make('color') 302 | ] : []; 303 | }) 304 | ->createOptionUsing(function ($data){ 305 | self::getCategoryModel()::query()->create($data); 306 | }) 307 | ]), 308 | Section::make('Attachments')->schema([ 309 | FileUpload::make('attachments') 310 | ->multiple() 311 | ->preserveFilenames() 312 | ->disablePreview() 313 | ->disableLabel() 314 | ->enableDownload() 315 | ->enableOpen() 316 | ]) 317 | ->heading(trans('timex::timex.event.attachments')) 318 | ->hidden(!in_array('attachments',\Schema::getColumnListing(self::getEventTableName()))) 319 | ->collapsible() 320 | ->compact() 321 | ->collapsed(function ($get){ 322 | return $get('attachments') == null ? true : false; 323 | }), 324 | ])->columnSpan(1), 325 | ]), 326 | ]; 327 | } 328 | 329 | public static function table(Table $table): Table 330 | { 331 | return $table 332 | ->columns([ 333 | TextColumn::make('subject') 334 | ->label(trans('timex::timex.event.subject')), 335 | TextColumn::make('body') 336 | ->label(trans('timex::timex.event.body')) 337 | ->wrap() 338 | ->limit(100), 339 | TextColumn::make('start') 340 | ->label(trans('timex::timex.event.start')) 341 | ->date() 342 | ->description(fn($record) => $record->startTime), 343 | TextColumn::make('end') 344 | ->label(trans('timex::timex.event.end')) 345 | ->date() 346 | ->description(fn($record)=> $record->endTime), 347 | BadgeColumn::make('category') 348 | ->label(trans('timex::timex.event.category')) 349 | ->enum(config('timex.categories.labels')) 350 | ->formatStateUsing(function ($record){ 351 | if (\Str::isUuid($record->category)){ 352 | return self::getCategoryModel() == null ? "" : self::getCategoryModel()::findOrFail($record->category)->getAttributes()[self::getCategoryModelColumn('value')]; 353 | }else{ 354 | return config('timex.categories.labels')[$record->category] ?? ""; 355 | } 356 | }) 357 | ->color(function ($record){ 358 | if (\Str::isUuid($record->category)){ 359 | return self::getCategoryModel() == null ? "primary" :self::getCategoryModel()::findOrFail($record->category)->getAttributes()[self::getCategoryModelColumn('color')]; 360 | }else{ 361 | return config('timex.categories.colors')[$record->category] ?? "primary"; 362 | } 363 | }) 364 | ])->defaultSort('start') 365 | ->bulkActions([ 366 | DeleteBulkAction::make()->action(function (Collection $records){ 367 | return $records->each(function ($record){ 368 | return $record->organizer == \Auth::id() ? $record->delete() : ''; 369 | }); 370 | }) 371 | ]); 372 | } 373 | 374 | public static function getPages(): array 375 | { 376 | return [ 377 | 'index' => Pages\ListEvents::route('/'), 378 | 'create' => Pages\CreateEvent::route('/create'), 379 | 'edit' => Pages\EditEvent::route('/{record}/edit'), 380 | ]; 381 | } 382 | 383 | public static function getGloballySearchableAttributes(): array 384 | { 385 | return []; 386 | } 387 | 388 | public static function canEdit(Model $record): bool 389 | { 390 | return $record->organizer == \Auth::id(); 391 | } 392 | 393 | public static function canDelete(Model $record): bool 394 | { 395 | return $record->organizer == \Auth::id(); 396 | } 397 | 398 | public static function canForceDelete(Model $record): bool 399 | { 400 | return $record->organizer == \Auth::id(); 401 | } 402 | 403 | } 404 | --------------------------------------------------------------------------------