From 0f96106a5612ea67a872a4185df4a75a2b417ce1 Mon Sep 17 00:00:00 2001 From: Lokman Musliu Date: Tue, 8 Feb 2022 15:30:51 +0100 Subject: [PATCH] =?UTF-8?q?First=20version=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + README.md | 136 ++++++++++ composer.json | 19 ++ config/business_hours.php | 6 + content/addons/business_hours.yaml | 43 ++++ resources/lang/en/fieldsets/defaults.php | 21 ++ resources/lang/en/settings.php | 6 + resources/views/cp/settings/index.blade.php | 13 + routes/cp.php | 11 + src/Blueprints/BusinessHours.php | 235 ++++++++++++++++++ .../Controllers/BusinessHoursController.php | 43 ++++ src/ServiceProvider.php | 43 ++++ src/Tags/BusinessHoursTags.php | 141 +++++++++++ 13 files changed, 720 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 composer.json create mode 100644 config/business_hours.php create mode 100644 content/addons/business_hours.yaml create mode 100644 resources/lang/en/fieldsets/defaults.php create mode 100644 resources/lang/en/settings.php create mode 100644 resources/views/cp/settings/index.blade.php create mode 100644 routes/cp.php create mode 100644 src/Blueprints/BusinessHours.php create mode 100644 src/Http/Controllers/BusinessHoursController.php create mode 100644 src/ServiceProvider.php create mode 100644 src/Tags/BusinessHoursTags.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0199b2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +vendor +mix-manifest.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8ef42f --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# Business Hours + + Business Hours is a Statamic addon that gives you the option to easily create, manage and display business hours in a modern and stylish layout. + +## Features + +- Set opening and closing times for each day of the week separately. +- Fields to indicate if you are currently open or closed +- Conditionals to display a text only when you are open or closed +- Comes unstyled, bring your own styling +- Supports `special dates` e.g: holidays, company wide vacations etc +- Use different time zones and languages +- Beautiful & user-friendly settings screen for you, or your client +- Localizable fields for easy translation in any language + +## How to Install + +You can search for this addon in the `Tools > Addons` section of the Statamic control panel and click **install**, or run the following command from your project root: + +``` bash +composer require lucky-media/business-hours +``` + +## How to Use + +On your frontend you can make use of the `business_hours` tag to iterate through the hours. + + +``` +{{ business_hours }} + + {{ weekday }} + + {{ closed }} + + {{ 24_hours }} + + {{ start_time }} + + {{ end_time }} + + {{ is_open }} + +{{ /business_hours }} +``` + +Params: +- `weekday` (string) +- - Weekday label +- `closed` (boolean) +- - Is the business closed on this day +- `24_hours` (boolean) +- - Is the business working 24hrs on this day +- `start_time` (string) +- - What time does the business start on this day +- `end_time` (string) +- - What time does the business end on this day +- `is_open` (boolean) +- - Is the business open at this moment + + +If you need to display the exeptions you can use the following: + +``` +{{ business_hours:exception }} + + {{ reason }} + + {{ start_date }} + + {{ end_date }} + +{{ /business_hours:exception }} +``` + +Params: +- `reason` (bard) +- - Bard field to write down the reason for the exception +- `start_date` (date) +- - Start date of the exception +- `end_date` (date) +- - End date of the exception + + +## Common Gotcha's +- Make sure on your `config/app.php` you have the correct timezone. +- You can either display times on 24 hour format or 12 hour format, you can change this on `config/statamic/business_hours.php` +- Make sure on CP the `start_time` and `end_time` are entered in 24 hour format. There is also validation if the time is not in this format. + +## Examples +Check the example below and the code snippet. Please keep in mind that the examples use Tailwind CSS for styling. + +![Screenshot 2022-02-08 at 13 21 11](https://user-images.githubusercontent.com/11158157/152985979-aec8c318-7774-419d-b591-c44a66ab544a.png) + +``` +
+ {{ business_hours:exception }} +
+ {{ reason }} +
+ {{ /business_hours:exception }} + + +
+ {{ business_hours }} +

+ {{ weekday }} +

+ +

+ {{ if closed }} + closed + {{ /if }} + + {{ if !closed and !24_hours }} + + {{ start_time }} - {{ end_time }} + + {{ /if }} + + {{ if 24_hours }} + + 24hrs + + {{ /if }} + + {{ if is_open }} + + open now + + {{ /if }} +

+ {{ /business_hours }} +
+
+``` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..44c1ce6 --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "name": "lucky-media/business-hours", + "autoload": { + "psr-4": { + "LuckyMedia\\BusinessHours\\": "src" + } + }, + "extra": { + "statamic": { + "name": "Business Hours", + "description": "Easily create, manage and display business hours in a modern and stylish layout." + }, + "laravel": { + "providers": [ + "LuckyMedia\\BusinessHours\\ServiceProvider" + ] + } + } +} diff --git a/config/business_hours.php b/config/business_hours.php new file mode 100644 index 0000000..d38497a --- /dev/null +++ b/config/business_hours.php @@ -0,0 +1,6 @@ + base_path('content/addons/business_hours.yaml'), + 'time_format' => 24 // 12 for AM / PM +]; diff --git a/content/addons/business_hours.yaml b/content/addons/business_hours.yaml new file mode 100644 index 0000000..aa2c4de --- /dev/null +++ b/content/addons/business_hours.yaml @@ -0,0 +1,43 @@ +hours: + - + weekday: Monday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: false + - + weekday: Tuesday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: false + - + weekday: Wednesday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: false + - + weekday: Thursday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: false + - + weekday: Friday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: false + - + weekday: Saturday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: true + - + weekday: Sunday + start_time: '08:00' + end_time: '16:00' + 24_hours: false + closed: true diff --git a/resources/lang/en/fieldsets/defaults.php b/resources/lang/en/fieldsets/defaults.php new file mode 100644 index 0000000..358a034 --- /dev/null +++ b/resources/lang/en/fieldsets/defaults.php @@ -0,0 +1,21 @@ + 'General', + 'exceptions' => 'Exceptions', + 'hours' => 'Hours', + 'enabled' => 'Enabled', + 'weekday' => 'Weekday', + 'start_time' => 'Start Time', + 'end_time' => 'End Time', + '24_hours' => '24 Hours', + 'closed' => 'Closed', + 'enable_date' => 'Enable', + 'enable_date_instr' => 'Toggle for this date to be active', + 'start_date' => 'Start Date', + 'start_date_instr' => 'When should the exception start', + 'end_date' => 'End Date', + 'end_date_instr' => 'When should the exception end', + 'reason' => 'Reason', + 'add_row' => 'New Date' +]; diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php new file mode 100644 index 0000000..ce3bfd7 --- /dev/null +++ b/resources/lang/en/settings.php @@ -0,0 +1,6 @@ + 'Business Hours', +]; diff --git a/resources/views/cp/settings/index.blade.php b/resources/views/cp/settings/index.blade.php new file mode 100644 index 0000000..64dbbad --- /dev/null +++ b/resources/views/cp/settings/index.blade.php @@ -0,0 +1,13 @@ +@extends('statamic::layout') +@section('title', __('business-hours::settings.name')) + +@section('content') + +@endsection diff --git a/routes/cp.php b/routes/cp.php new file mode 100644 index 0000000..b4ee937 --- /dev/null +++ b/routes/cp.php @@ -0,0 +1,11 @@ +prefix('business-hours/') + ->name('luckymedia.businesshours.') + ->group(function () { + Route::get('/', 'BusinessHoursController@index')->name('index'); + Route::put('/', 'BusinessHoursController@update')->name('update'); +}); diff --git a/src/Blueprints/BusinessHours.php b/src/Blueprints/BusinessHours.php new file mode 100644 index 0000000..1508519 --- /dev/null +++ b/src/Blueprints/BusinessHours.php @@ -0,0 +1,235 @@ +fields() + ->addValues(static::values()) + ->augment() + ->values(); + } + + public static function values() + { + return collect(YAML::file(config('statamic.business_hours.path'))->parse()) + ->merge(YAML::file(config('statamic.business_hours.path'))->parse()) + ->all(); + } + + public static function blueprint() + { + return Blueprint::make()->setContents([ + 'sections' => [ + 'main' => [ + 'display' => __('business-hours::fieldsets/defaults.general'), + 'fields' => [ + [ + 'handle' => 'hours', + 'field' => [ + 'fields' => [ + [ + 'handle' => 'enabled', + 'field' => [ + 'default' => true, + 'display' => __('business-hours::fieldsets/defaults.enabled'), + 'type' => 'toggle', + 'icon' => 'toggle', + 'width' => 25, + ], + ], + [ + 'handle' => 'weekday', + 'field' => [ + 'display' => __('business-hours::fieldsets/defaults.weekday'), + 'type' => 'text', + 'input_type' => 'text', + 'icon' => 'text', + 'width' => 25, + 'unless' => [ + 'enabled' => 'equals false' + ], + ], + ], + [ + 'handle' => 'start_time', + 'field' => [ + 'display' => __('business-hours::fieldsets/defaults.start_time'), + 'type' => 'text', + 'input_type' => 'text', + 'icon' => 'text', + 'width' => 25, + 'unless_any' => [ + 'enabled' => 'equals false', + 'closed' => 'equals true', + '24_hours' => 'equals true', + ], + 'validate' => [ + 'date_format:H:i', + ], + ], + ], + [ + 'handle' => 'end_time', + 'input_type' => 'text', + 'field' => [ + 'display' => __('business-hours::fieldsets/defaults.end_time'), + 'type' => 'text', + 'input_type' => 'text', + 'icon' => 'text', + 'width' => 25, + 'unless_any' => [ + 'enabled' => 'equals false', + 'closed' => 'equals true', + '24_hours' => 'equals true', + ], + 'validate' => [ + 'date_format:H:i', + ], + ], + ], + [ + 'handle' => '24_hours', + 'field' => [ + 'default' => false, + 'display' => __('business-hours::fieldsets/defaults.24_hours'), + 'type' => 'toggle', + 'icon' => 'toggle', + 'width' => 25, + 'unless_any' => [ + 'enabled' => 'equals false', + 'closed' => 'equals true', + ], + ], + ], + [ + 'handle' => 'closed', + 'field' => [ + 'default' => false, + 'display' => __('business-hours::fieldsets/defaults.closed'), + 'type' => 'toggle', + 'icon' => 'toggle', + 'unless_any' => [ + 'enabled' => 'equals false', + '24_hours' => 'equals true', + ] + ], + ], + ], + 'mode' => 'table', + 'min_rows' => 7, + 'max_rows' => 7, + 'reorderable' => false, + 'display' => __('business-hours::fieldsets/defaults.hours'), + 'type' => 'grid', + 'icon' => 'grid', + ], + ], + ], + ], + 'exceptions' => [ + 'display' => __('business-hours::fieldsets/defaults.exceptions'), + 'fields' => [ + [ + 'handle' => 'exceptions', + 'field' => [ + 'fields' => [ + [ + 'handle' => 'enable_date', + 'field' => [ + 'default' => true, + 'display' => __('business-hours::fieldsets/defaults.enable_date'), + 'instructions' => __('business-hours::fieldsets/defaults.enable_date_instr'), + 'type' => 'toggle', + 'icon' => 'toggle', + 'width' => 100, + ], + ], + [ + 'handle' => 'start_date', + 'field' => [ + 'mode' => 'single', + 'time_enabled' => false, + 'time_required' => false, + 'full_width' => false, + 'inline' => false, + 'columns' => 1, + 'rows' => 1, + 'display' => __('business-hours::fieldsets/defaults.start_date'), + 'instructions' => __('business-hours::fieldsets/defaults.start_date_instr'), + 'type' => 'date', + 'icon' => 'date', + 'width' => 50, + 'validate' => [ + 'required', + ], + ], + ], + [ + 'handle' => 'end_date', + 'field' => [ + 'mode' => 'single', + 'time_enabled' => false, + 'time_required' => false, + 'full_width' => false, + 'inline' => false, + 'columns' => 1, + 'rows' => 1, + 'display' => __('business-hours::fieldsets/defaults.end_date'), + 'instructions' => __('business-hours::fieldsets/defaults.end_date_instr'), + 'type' => 'date', + 'icon' => 'date', + 'width' => 50, + 'validate' => [ + 'required', + ], + ], + ], + [ + 'handle' => 'reason', + 'field' => [ + 'always_show_set_button' => false, + 'buttons' => [ + 0 => 'bold', + 1 => 'italic', + 2 => 'removeformat', + ], + 'save_html' => false, + 'toolbar_mode' => 'fixed', + 'link_noopener' => false, + 'link_noreferrer' => false, + 'target_blank' => false, + 'reading_time' => false, + 'fullscreen' => false, + 'allow_source' => false, + 'enable_input_rules' => false, + 'enable_paste_rules' => true, + 'antlers' => false, + 'display' => __('business-hours::fieldsets/defaults.reason'), + 'type' => 'bard', + 'icon' => 'bard', + ], + ], + ], + 'mode' => 'stacked', + 'add_row' => __('business-hours::fieldsets/defaults.add_row'), + 'reorderable' => true, + 'display' => __('business-hours::fieldsets/defaults.exceptions'), + 'type' => 'grid', + 'icon' => 'grid', + 'listable' => 'hidden', + 'instructions_position' => 'above', + ], + ] + ] + ] + ], + ]); + } +} diff --git a/src/Http/Controllers/BusinessHoursController.php b/src/Http/Controllers/BusinessHoursController.php new file mode 100644 index 0000000..1dfd158 --- /dev/null +++ b/src/Http/Controllers/BusinessHoursController.php @@ -0,0 +1,43 @@ +fields()->addValues($data)->preProcess(); + + return view('business-hours::cp.settings.index', [ + 'blueprint' => $blueprint->toPublishArray(), + 'values' => $fields->values(), + 'meta' => $fields->meta(), + ]); + } + + public function update(Request $request) + { + $blueprint = BusinessHours::blueprint(); + + $fields = $blueprint->fields()->addValues($request->all()); + + $fields->validate(); + + File::put(config('statamic.business_hours.path'), YAML::dump($fields->process()->values()->all())); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php new file mode 100644 index 0000000..722c8ff --- /dev/null +++ b/src/ServiceProvider.php @@ -0,0 +1,43 @@ + __DIR__.'/../routes/cp.php', + ]; + + protected $tags = [ + BusinessHoursTags::class, + ]; + + protected $translations = true; + + public function bootAddon() + { + parent::boot(); + + Nav::extend(function ($nav) { + $nav->content(__("business-hours::settings.name")) + ->route('luckymedia.businesshours.index') + ->icon('time'); + }); + + $this->mergeConfigFrom(__DIR__.'/../config/business_hours.php', 'statamic.business_hours'); + + $this->publishes([ + __DIR__.'/../config/business_hours.php' => config_path('statamic/business_hours.php'), + ], 'luckymedia-businesshours-config'); + + if (!file_exists(base_path(config('statamic.business_hours.path')))) { + $this->publishes([ + __DIR__.'/../content/addons/business_hours.yaml' => config('statamic.business_hours.path'), + ], 'luckymedia-businesshours-content'); + }; + } +} diff --git a/src/Tags/BusinessHoursTags.php b/src/Tags/BusinessHoursTags.php new file mode 100644 index 0000000..2417736 --- /dev/null +++ b/src/Tags/BusinessHoursTags.php @@ -0,0 +1,141 @@ +defaults = BusinessHours::augmentedValues(); + + $this->time_format = config('statamic.business_hours.time_format'); + } + + public function index(): array + { + $days = collect($this->defaults['hours']->value()) + ->map(function ($day, $key) { + return [ + 'is_open' => $this->isOpen($day, $key), + 'is_past' => $this->isPast($day, $key), + 'enabled' => $day['enabled']->value(), + 'weekday' => $day['weekday']->value(), + 'start_time' => $this->formatDate($day['start_time']->value()), + 'end_time' => $this->formatDate($day['end_time']->value()), + '24_hours' => $day['24_hours']->value(), + 'closed' => $day['closed']->value(), + ]; + }) + ->filter(function($day) { + return $day['enabled'] === true; + }); + + return $days->toArray(); + } + + public function exception() + { + $days = collect($this->defaults['exceptions']->value()) + ->first(function ($day) { + $is_enabled = $day['enable_date']->value(); + $start_date = Carbon::create($day['start_date']->value()); + $end_date = Carbon::create($day['end_date']->value()); + + if (! $is_enabled) return false; + + if ($start_date->isToday() || $end_date->isToday()) return true; + + if (! $start_date->isPast()) return false; + + if (! $end_date->isFuture()) return false; + + return false; + }); + + if (empty($days)) { + return false; + } + + return $days; + } + + public function exceptions(): array + { + return $this->defaults['exceptions']->value(); + } + + protected function isPast($day, $key): bool + { + if ($this->isToday($key)) { + $end_today = $this->formatToTime($day['end_time']); + + if (! now()->gt($end_today)) { + return false; + } + + return true; + } + + if (! (($key + 1) < now()->dayOfWeekIso)) return false; + + return true; + } + + protected function isOpen($day, $key): bool + { + $start_today = $this->formatToTime($day['start_time']); + $end_today = $this->formatToTime($day['end_time']); + + if (! $this->checkIfOpen($key, $start_today, $end_today)) { + return false; + } + + return true; + } + + protected function checkIfOpen($key, $start, $end): bool + { + + if (! $this->isToday($key)) { + return false; + } + + if (! empty($this->exception())) { + return false; + } + + if(! now()->between($start, $end)) { + return false; + } + + return true; + } + + protected function isToday($key): bool + { + return ($key + 1) === now()->dayOfWeekIso; + } + + protected function formatToTime($day) { + [$hour, $minute] = explode(':', $day->value()); + + return Carbon::createFromTime($hour, $minute); + } + + protected function formatDate(string $day): string + { + if ($this->time_format === 12) { + return Carbon::create($day)->format('g:i a'); + } + + return Carbon::create($day)->format('H:i'); + } +}