diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 0b28540..f0d611f 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -31,6 +31,7 @@ You can also use our sample fieldset by importing `events::event`. * all the single day fields * `recurrence` - **Optional** - One of `daily`, `weekly`, `monthly`, `annually`, `every` +* `specific_days` - **Optional** - when `recurrence` is `monthly`, you can choose options like every 3rd Tuesday, etc. You can have more than one. * `interval` - **Optional** - required if `recurrence` is `every` and indicates the frequency of the event * `period` - **Optional** - required if `recurrence` is `every` and indicates the period of recurrence. One of `days`, `weeks`, `months`, `years` * `end_date` - **Optional** - when is the last event. If `recurrence` is set and this is not, the event goes on forever diff --git a/resources/fieldsets/event.yaml b/resources/fieldsets/event.yaml index aa66414..4057b7b 100644 --- a/resources/fieldsets/event.yaml +++ b/resources/fieldsets/event.yaml @@ -14,17 +14,6 @@ fields: width: 33 display: Recurrence default: none - instructions_position: above - listable: hidden - visibility: visible - replicator_preview: true - taggable: false - push_tags: false - multiple: false - clearable: false - searchable: true - cast_booleans: false - hide_display: false - handle: timezone field: @@ -36,9 +25,6 @@ fields: cast_booleans: false display: Timezone type: timezones - icon: select - listable: hidden - instructions_position: above mode: typeahead width: 33 - @@ -47,14 +33,17 @@ fields: type: toggle width: 33 display: 'All Day?' - instructions_position: above - listable: hidden - visibility: visible - replicator_preview: true - default: false - hide_display: false unless: recurrence: 'equals multi_day' + - + handle: specific_days + field: + dictionary: month_day_recurrence + type: dictionary + display: 'Which Day(s)' + width: 100 + if: + recurrence: 'equals monthly' - handle: interval field: @@ -85,18 +74,8 @@ fields: input_format: M/D/YYYY width: 50 display: 'Start Date' - instructions_position: above - listable: hidden - visibility: visible - replicator_preview: true - mode: single inline: true full_width: true - columns: 1 - rows: 1 - time_enabled: false - time_seconds_enabled: false - hide_display: false unless_any: multi_day: 'equals true' recurrence: 'equals multi_day' @@ -110,19 +89,9 @@ fields: input_format: M/D/YYYY display: 'End Date' width: 50 - listable: hidden - mode: single - time_enabled: false time_required: false full_width: true inline: true - columns: 1 - rows: 1 - instructions_position: above - visibility: visible - replicator_preview: true - time_seconds_enabled: false - hide_display: false if: recurrence: 'contains_any daily, weekly, monthly, every' - @@ -132,12 +101,6 @@ fields: width: 25 display: 'Start Time' instructions: 'Input in [24-hour format](https://en.wikipedia.org/wiki/24-hour_clock)' - instructions_position: above - listable: hidden - visibility: visible - replicator_preview: true - seconds_enabled: false - hide_display: false unless_any: multi_day: 'equals true' all_day: 'equals true' @@ -149,12 +112,6 @@ fields: width: 25 display: 'End Time' instructions: 'Input in [24-hour format](https://en.wikipedia.org/wiki/24-hour_clock)' - instructions_position: above - listable: hidden - visibility: visible - replicator_preview: true - seconds_enabled: false - hide_display: false unless_any: multi_day: 'equals true' all_day: 'equals true' @@ -163,7 +120,12 @@ fields: handle: days field: type: grid - mode: table + display: 'Event Days' + add_row: 'Add Day' + min_rows: 1 + if_any: + multi_day: 'equals true' + recurrence: 'equals multi_day' fields: - handle: date @@ -210,24 +172,14 @@ fields: field: 'events::event.all_day' config: width: 25 - display: 'Event Days' - add_row: 'Add Day' - listable: hidden - reorderable: true - instructions_position: above - visibility: visible - replicator_preview: true - min_rows: 1 - fullscreen: true - hide_display: false - if_any: - multi_day: 'equals true' - recurrence: 'equals multi_day' - handle: exclude_dates field: type: grid - mode: table + display: 'Exclude Days' + add_row: 'Add Day' + if_any: + recurrence: 'contains_any monthly, daily, weekly, every' fields: - handle: date @@ -238,14 +190,3 @@ fields: require_time: false input_format: M/D/YYYY display: Date - display: 'Exclude Days' - add_row: 'Add Day' - listable: hidden - reorderable: true - instructions_position: above - visibility: visible - replicator_preview: true - fullscreen: true - hide_display: false - if_any: - recurrence: 'contains_any monthly, daily, weekly, every' diff --git a/src/Dictionaries/MonthDayRecurrence.php b/src/Dictionaries/MonthDayRecurrence.php new file mode 100644 index 0000000..20479ec --- /dev/null +++ b/src/Dictionaries/MonthDayRecurrence.php @@ -0,0 +1,56 @@ + 'First Sunday', 'value' => 'first_sunday', 'rrule' => '1SU'], + ['label' => 'First Monday', 'value' => 'first_monday', 'rrule' => '1MO'], + ['label' => 'First Tuesday', 'value' => 'first_tuesday', 'rrule' => '1TU'], + ['label' => 'First Wednesday', 'value' => 'first_wednesday', 'rrule' => '1WE'], + ['label' => 'First Thursday', 'value' => 'first_thursday', 'rrule' => '1TH'], + ['label' => 'First Friday', 'value' => 'first_friday', 'rrule' => '1FR'], + ['label' => 'First Saturday', 'value' => 'first_saturday', 'rrule' => '1SA'], + ['label' => 'Second Sunday', 'value' => 'second_sunday', 'rrule' => '2SU'], + ['label' => 'Second Monday', 'value' => 'second_monday', 'rrule' => '2MO'], + ['label' => 'Second Tuesday', 'value' => 'second_tuesday', 'rrule' => '2TU'], + ['label' => 'Second Wednesday', 'value' => 'second_wednesday', 'rrule' => '2WE'], + ['label' => 'Second Thursday', 'value' => 'second_thursday', 'rrule' => '2TH'], + ['label' => 'Second Friday', 'value' => 'second_friday', 'rrule' => '2FR'], + ['label' => 'Second Saturday', 'value' => 'second_saturday', 'rrule' => '2SA'], + ['label' => 'Third Sunday', 'value' => 'third_sunday', 'rrule' => '3SU'], + ['label' => 'Third Monday', 'value' => 'third_monday', 'rrule' => '3MO'], + ['label' => 'Third Tuesday', 'value' => 'third_tuesday', 'rrule' => '3TU'], + ['label' => 'Third Wednesday', 'value' => 'third_wednesday', 'rrule' => '3WE'], + ['label' => 'Third Thursday', 'value' => 'third_thursday', 'rrule' => '3TH'], + ['label' => 'Third Friday', 'value' => 'third_friday', 'rrule' => '3FR'], + ['label' => 'Third Saturday', 'value' => 'third_saturday', 'rrule' => '3SA'], + ['label' => 'Fourth Sunday', 'value' => 'fourth_sunday', 'rrule' => '4SU'], + ['label' => 'Fourth Monday', 'value' => 'fourth_monday', 'rrule' => '4MO'], + ['label' => 'Fourth Tuesday', 'value' => 'fourth_tuesday', 'rrule' => '4TU'], + ['label' => 'Fourth Wednesday', 'value' => 'fourth_wednesday', 'rrule' => '4WE'], + ['label' => 'Fourth Thursday', 'value' => 'fourth_thursday', 'rrule' => '4TH'], + ['label' => 'Fourth Friday', 'value' => 'fourth_friday', 'rrule' => '4FR'], + ['label' => 'Fourth Saturday', 'value' => 'fourth_saturday', 'rrule' => '4SA'], + ['label' => 'Fifth Sunday', 'value' => 'fifth_sunday', 'rrule' => '5SU'], + ['label' => 'Fifth Monday', 'value' => 'fifth_monday', 'rrule' => '5MO'], + ['label' => 'Fifth Tuesday', 'value' => 'fifth_tuesday', 'rrule' => '5TU'], + ['label' => 'Fifth Wednesday', 'value' => 'fifth_wednesday', 'rrule' => '5WE'], + ['label' => 'Fifth Thursday', 'value' => 'fifth_thursday', 'rrule' => '5TH'], + ['label' => 'Fifth Friday', 'value' => 'fifth_friday', 'rrule' => '5FR'], + ['label' => 'Fifth Saturday', 'value' => 'fifth_saturday', 'rrule' => '5SA'], + ['label' => 'Last Sunday', 'value' => 'last_sunday', 'rrule' => '-1SU'], + ['label' => 'Last Monday', 'value' => 'last_monday', 'rrule' => '-1MO'], + ['label' => 'Last Tuesday', 'value' => 'last_tuesday', 'rrule' => '-1TU'], + ['label' => 'Last Wednesday', 'value' => 'last_wednesday', 'rrule' => '-1WE'], + ['label' => 'Last Thursday', 'value' => 'last_thursday', 'rrule' => '-1TH'], + ['label' => 'Last Friday', 'value' => 'last_friday', 'rrule' => '-1FR'], + ['label' => 'Last Saturday', 'value' => 'last_saturday', 'rrule' => '-1SA'], + ]; + } +} diff --git a/src/Types/RecurringEvent.php b/src/Types/RecurringEvent.php index 5a19573..6af7fe8 100644 --- a/src/Types/RecurringEvent.php +++ b/src/Types/RecurringEvent.php @@ -2,6 +2,7 @@ namespace TransformStudios\Events\Types; +use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use RRule\RRule; use RRule\RRuleInterface; @@ -11,6 +12,11 @@ class RecurringEvent extends Event { + public function onSpecificDays(): array + { + return $this->specific_days ?? []; + } + public function interval(): int { return $this->interval ?? 1; @@ -44,6 +50,10 @@ protected function rule(): RRuleInterface $rule['until'] = Carbon::parse($end)->shiftTimezone($this->timezone['timezone'])->endOfDay(); } + if (! empty($days = $this->onSpecificDays())) { + $rule['byday'] = Arr::pluck($days, 'rrule'); + } + return new RRule($rule); } diff --git a/tests/Unit/RecurringEventsTest.php b/tests/Unit/RecurringEventsTest.php index c5a80c7..74b1887 100755 --- a/tests/Unit/RecurringEventsTest.php +++ b/tests/Unit/RecurringEventsTest.php @@ -14,7 +14,7 @@ class RecurringEventsTest extends TestCase { #[Test] - public function canCreateRecurringEvent() + public function can_create_recurring_event() { $recurringEntry = Entry::make() ->collection('events') @@ -32,7 +32,7 @@ public function canCreateRecurringEvent() } #[Test] - public function wontCreateRecurringEventWhenMultiDay() + public function wont_create_recurring_event_when_multi_day() { $recurringEntry = Entry::make() ->collection('events') @@ -50,7 +50,7 @@ public function wontCreateRecurringEventWhenMultiDay() } #[Test] - public function canShowLastOccurrenceWhenNoEndTime() + public function can_show_last_occurrence_when_no_end_time() { Carbon::setTestNow(now()->setTimeFromTimeString('10:00')); @@ -69,4 +69,26 @@ public function canShowLastOccurrenceWhenNoEndTime() $this->assertCount(2, $occurrences); } + + #[Test] + public function can_generate_monthly_by_day_occurrences() + { + Carbon::setTestNow(Carbon::parse('Jan 29 2025 10:00am')); + + $recurringEntry = tap(Entry::make() + ->collection('events') + ->data([ + 'start_date' => ray()->pass(Carbon::now()->addDays(1)->toDateString()), + 'start_time' => '22:00', + 'recurrence' => 'monthly', + 'end_date' => ray()->pass(Carbon::now()->addMonths(3)->toDateString()), + 'timezone' => 'America/Chicago', + 'specific_days' => ['first_sunday', 'last_wednesday'], + ]))->save(); + + $occurrences = Events::fromCollection(handle: 'events') + ->between(Carbon::now(), Carbon::now()->addMonths(4)); + + $this->assertCount(5, $occurrences); + } }