mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-23 12:52:20 +01:00
(wip) add event type
This commit is contained in:
parent
6888f00036
commit
aed5d1fc1b
@ -96,7 +96,7 @@ class API::EventsController < API::APIController
|
|||||||
# handle general properties
|
# handle general properties
|
||||||
event_preparams = params.required(:event).permit(:title, :description, :start_date, :start_time, :end_date, :end_time,
|
event_preparams = params.required(:event).permit(:title, :description, :start_date, :start_time, :end_date, :end_time,
|
||||||
:amount, :nb_total_places, :availability_id, :all_day, :recurrence,
|
:amount, :nb_total_places, :availability_id, :all_day, :recurrence,
|
||||||
:recurrence_end_at, :category_id, :event_theme_ids, :age_range_id, :booking_nominative,
|
:recurrence_end_at, :category_id, :event_theme_ids, :age_range_id, :event_type,
|
||||||
event_theme_ids: [],
|
event_theme_ids: [],
|
||||||
event_image_attributes: %i[id attachment],
|
event_image_attributes: %i[id attachment],
|
||||||
event_files_attributes: %i[id attachment _destroy],
|
event_files_attributes: %i[id attachment _destroy],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { SubmitHandler, useFieldArray, useForm, useWatch } from 'react-hook-form';
|
import { SubmitHandler, useFieldArray, useForm, useWatch } from 'react-hook-form';
|
||||||
import { Event, EventDecoration, EventPriceCategoryAttributes, RecurrenceOption } from '../../models/event';
|
import { Event, EventDecoration, EventPriceCategoryAttributes, RecurrenceOption, EventType } from '../../models/event';
|
||||||
import EventAPI from '../../api/event';
|
import EventAPI from '../../api/event';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
@ -40,7 +40,7 @@ interface EventFormProps {
|
|||||||
* Form to edit or create events
|
* Form to edit or create events
|
||||||
*/
|
*/
|
||||||
export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, onSuccess }) => {
|
export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, onSuccess }) => {
|
||||||
const { handleSubmit, register, control, setValue, formState } = useForm<Event>({ defaultValues: { ...event } });
|
const { handleSubmit, register, control, setValue, formState } = useForm<Event>({ defaultValues: Object.assign({ event_type: 'standard' }, event) });
|
||||||
const output = useWatch<Event>({ control });
|
const output = useWatch<Event>({ control });
|
||||||
const { fields, append, remove } = useFieldArray({ control, name: 'event_price_categories_attributes' });
|
const { fields, append, remove } = useFieldArray({ control, name: 'event_price_categories_attributes' });
|
||||||
|
|
||||||
@ -168,6 +168,17 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method provides event type options
|
||||||
|
*/
|
||||||
|
const buildEventTypeOptions = (): Array<SelectOption<EventType>> => {
|
||||||
|
return [
|
||||||
|
{ label: t('app.admin.event_form.event_types.standard'), value: 'standard' },
|
||||||
|
{ label: t('app.admin.event_form.event_types.nominative'), value: 'nominative' },
|
||||||
|
{ label: t('app.admin.event_form.event_types.family'), value: 'family' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="event-form">
|
<div className="event-form">
|
||||||
<header>
|
<header>
|
||||||
@ -203,6 +214,12 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
|||||||
label={t('app.admin.event_form.description')}
|
label={t('app.admin.event_form.description')}
|
||||||
limit={null}
|
limit={null}
|
||||||
heading bulletList blockquote link video image />
|
heading bulletList blockquote link video image />
|
||||||
|
<FormSelect id="event_type"
|
||||||
|
control={control}
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.admin.event_form.event_type')}
|
||||||
|
options={buildEventTypeOptions()}
|
||||||
|
rules={{ required: true }} />
|
||||||
<FormSelect id="category_id"
|
<FormSelect id="category_id"
|
||||||
control={control}
|
control={control}
|
||||||
formState={formState}
|
formState={formState}
|
||||||
@ -290,11 +307,6 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
|||||||
label={t('app.admin.event_form.seats_available')}
|
label={t('app.admin.event_form.seats_available')}
|
||||||
type="number"
|
type="number"
|
||||||
tooltip={t('app.admin.event_form.seats_help')} />
|
tooltip={t('app.admin.event_form.seats_help')} />
|
||||||
<FormSwitch control={control}
|
|
||||||
id="booking_nominative"
|
|
||||||
label={t('app.admin.event_form.booking_nominative')}
|
|
||||||
formState={formState}
|
|
||||||
tooltip={t('app.admin.event_form.booking_nominative_help')} />
|
|
||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
type="number"
|
type="number"
|
||||||
id="amount"
|
id="amount"
|
||||||
|
@ -689,7 +689,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.booking_nominative) {
|
if (event.event_type === 'nominative' || event.event_type === 'family') {
|
||||||
for (const key of Object.keys($scope.reserve.bookingUsers)) {
|
for (const key of Object.keys($scope.reserve.bookingUsers)) {
|
||||||
for (const user of $scope.reserve.bookingUsers[key]) {
|
for (const user of $scope.reserve.bookingUsers[key]) {
|
||||||
reservation.booking_users_attributes.push({
|
reservation.booking_users_attributes.push({
|
||||||
|
@ -11,6 +11,7 @@ export interface EventPriceCategoryAttributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type RecurrenceOption = 'none' | 'day' | 'week' | 'month' | 'year';
|
export type RecurrenceOption = 'none' | 'day' | 'week' | 'month' | 'year';
|
||||||
|
export type EventType = 'standard' | 'nominative' | 'family';
|
||||||
|
|
||||||
export interface Event {
|
export interface Event {
|
||||||
id?: number,
|
id?: number,
|
||||||
@ -64,7 +65,7 @@ export interface Event {
|
|||||||
recurrence: RecurrenceOption,
|
recurrence: RecurrenceOption,
|
||||||
recurrence_end_at: Date,
|
recurrence_end_at: Date,
|
||||||
advanced_accounting_attributes?: AdvancedAccounting,
|
advanced_accounting_attributes?: AdvancedAccounting,
|
||||||
booking_nominative: boolean,
|
event_type: EventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventDecoration {
|
export interface EventDecoration {
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
<select ng-model="reserve.nbReservePlaces" ng-change="changeNbPlaces('normal')" ng-options="i for i in reserve.nbPlaces.normal">
|
<select ng-model="reserve.nbReservePlaces" ng-change="changeNbPlaces('normal')" ng-options="i for i in reserve.nbPlaces.normal">
|
||||||
</select> {{ 'app.public.events_show.ticket' | translate:{NUMBER:reserve.nbReservePlaces} }}
|
</select> {{ 'app.public.events_show.ticket' | translate:{NUMBER:reserve.nbReservePlaces} }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 m-b" ng-if="event.booking_nominative && reserve.nbReservePlaces > 0">
|
<div class="col-sm-12 m-b" ng-if="event.event_type === 'nominative' && reserve.nbReservePlaces > 0">
|
||||||
<div ng-repeat="user in reserve.bookingUsers.normal">
|
<div ng-repeat="user in reserve.bookingUsers.normal">
|
||||||
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
||||||
<input type="text" class="form-control" ng-model="user.name">
|
<input type="text" class="form-control" ng-model="user.name">
|
||||||
@ -132,7 +132,7 @@
|
|||||||
<select ng-model="reserve.tickets[price.id]" ng-change="changeNbPlaces(price.id)" ng-options="i for i in reserve.nbPlaces[price.id]">
|
<select ng-model="reserve.tickets[price.id]" ng-change="changeNbPlaces(price.id)" ng-options="i for i in reserve.nbPlaces[price.id]">
|
||||||
</select> {{ 'app.public.events_show.ticket' | translate:{NUMBER:reserve.tickets[price.id]} }}
|
</select> {{ 'app.public.events_show.ticket' | translate:{NUMBER:reserve.tickets[price.id]} }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 m-b" ng-if="event.booking_nominative && reserve.tickets[price.id] > 0">
|
<div class="col-sm-12 m-b" ng-if="event.event_type === 'nominative' && reserve.tickets[price.id] > 0">
|
||||||
<div ng-repeat="user in reserve.bookingUsers[price.id]">
|
<div ng-repeat="user in reserve.bookingUsers[price.id]">
|
||||||
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
||||||
<input type="text" class="form-control" ng-model="user.name">
|
<input type="text" class="form-control" ng-model="user.name">
|
||||||
|
@ -33,6 +33,8 @@ class Event < ApplicationRecord
|
|||||||
|
|
||||||
has_many :cart_item_event_reservations, class_name: 'CartItem::EventReservation', dependent: :destroy
|
has_many :cart_item_event_reservations, class_name: 'CartItem::EventReservation', dependent: :destroy
|
||||||
|
|
||||||
|
validates :event_type, inclusion: { in: %w[standard nominative family] }, presence: true
|
||||||
|
|
||||||
attr_accessor :recurrence, :recurrence_end_at
|
attr_accessor :recurrence, :recurrence_end_at
|
||||||
|
|
||||||
before_save :update_nb_free_places
|
before_save :update_nb_free_places
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
json.extract! event, :id, :title, :description, :booking_nominative
|
json.extract! event, :id, :title, :description, :event_type
|
||||||
if event.event_image
|
if event.event_image
|
||||||
json.event_image_attributes do
|
json.event_image_attributes do
|
||||||
json.id event.event_image.id
|
json.id event.event_image.id
|
||||||
|
@ -139,8 +139,6 @@ en:
|
|||||||
event_themes: "Event themes"
|
event_themes: "Event themes"
|
||||||
age_range: "Age range"
|
age_range: "Age range"
|
||||||
add_price: "Add a price"
|
add_price: "Add a price"
|
||||||
booking_nominative: "Nominative booking"
|
|
||||||
booking_nominative_help: "If you check this option, the members will have to enter the names of the participants when booking."
|
|
||||||
save: "Save"
|
save: "Save"
|
||||||
create_success: "The event was created successfully"
|
create_success: "The event was created successfully"
|
||||||
events_updated: "{COUNT, plural, =1{One event was} other{{COUNT} Events were}} successfully updated"
|
events_updated: "{COUNT, plural, =1{One event was} other{{COUNT} Events were}} successfully updated"
|
||||||
@ -153,6 +151,11 @@ en:
|
|||||||
every_week: "Every week"
|
every_week: "Every week"
|
||||||
every_month: "Every month"
|
every_month: "Every month"
|
||||||
every_year: "Every year"
|
every_year: "Every year"
|
||||||
|
event_type: "Event type"
|
||||||
|
event_types:
|
||||||
|
standard: "Event standard"
|
||||||
|
nominative: "Event nominative"
|
||||||
|
family: "Event family"
|
||||||
plan_form:
|
plan_form:
|
||||||
ACTION_title: "{ACTION, select, create{New} other{Update the}} plan"
|
ACTION_title: "{ACTION, select, create{New} other{Update the}} plan"
|
||||||
tab_settings: "Settings"
|
tab_settings: "Settings"
|
||||||
|
@ -139,8 +139,6 @@ fr:
|
|||||||
event_themes: "Thèmes de l'événement"
|
event_themes: "Thèmes de l'événement"
|
||||||
age_range: "Tranche d'âge"
|
age_range: "Tranche d'âge"
|
||||||
add_price: "Ajouter un tarif"
|
add_price: "Ajouter un tarif"
|
||||||
booking_nominative: "Réservation nominative"
|
|
||||||
booking_nominative_help: "Si cette option est activée, les réservations seront nominatives. Les participants devront s'identifier pour réserver."
|
|
||||||
save: "Enregistrer"
|
save: "Enregistrer"
|
||||||
create_success: "L'événement a bien été créé"
|
create_success: "L'événement a bien été créé"
|
||||||
events_updated: "{COUNT, plural, one {}=1{Un événement à été} other{{COUNT} événements ont été}} mis à jour avec succès"
|
events_updated: "{COUNT, plural, one {}=1{Un événement à été} other{{COUNT} événements ont été}} mis à jour avec succès"
|
||||||
@ -153,6 +151,11 @@ fr:
|
|||||||
every_week: "Chaque semaine"
|
every_week: "Chaque semaine"
|
||||||
every_month: "Chaque mois"
|
every_month: "Chaque mois"
|
||||||
every_year: "Chaque année"
|
every_year: "Chaque année"
|
||||||
|
event_type: "Type d'événement"
|
||||||
|
event_types:
|
||||||
|
standard: "Evénement standard"
|
||||||
|
nominative: "Evénement nominatif"
|
||||||
|
family: "Evénement famille"
|
||||||
plan_form:
|
plan_form:
|
||||||
ACTION_title: "{ACTION, select, create{Nouvelle} other{Mettre à jour la}} formule d'abonnement"
|
ACTION_title: "{ACTION, select, create{Nouvelle} other{Mettre à jour la}} formule d'abonnement"
|
||||||
tab_settings: "Paramètres"
|
tab_settings: "Paramètres"
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# add booking_nominative to event
|
|
||||||
class AddBookingNominativeToEvent < ActiveRecord::Migration[7.0]
|
|
||||||
def change
|
|
||||||
add_column :events, :booking_nominative, :boolean, default: false
|
|
||||||
end
|
|
||||||
end
|
|
@ -5,7 +5,8 @@ class CreateCartItemEventReservationBookingUsers < ActiveRecord::Migration[7.0]
|
|||||||
def change
|
def change
|
||||||
create_table :cart_item_event_reservation_booking_users do |t|
|
create_table :cart_item_event_reservation_booking_users do |t|
|
||||||
t.string :name
|
t.string :name
|
||||||
t.belongs_to :cart_item_event_reservation, foreign_key: true, index: { name: 'index_cart_item_booking_users_on_cart_item_event_reservation' }
|
t.belongs_to :cart_item_event_reservation, foreign_key: true,
|
||||||
|
index: { name: 'index_cart_item_booking_users_on_cart_item_event_reservation' }
|
||||||
t.references :event_price_category, foreign_key: true, index: { name: 'index_cart_item_booking_users_on_event_price_category' }
|
t.references :event_price_category, foreign_key: true, index: { name: 'index_cart_item_booking_users_on_event_price_category' }
|
||||||
t.references :booked, polymorphic: true
|
t.references :booked, polymorphic: true
|
||||||
|
|
||||||
|
10
db/migrate/20230511081018_add_event_type_to_event.rb
Normal file
10
db/migrate/20230511081018_add_event_type_to_event.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Add event_type to event model, to be able to create standard/nominative/family events
|
||||||
|
class AddEventTypeToEvent < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :events, :event_type, :string, default: 'standard'
|
||||||
|
Event.reset_column_information
|
||||||
|
Event.update_all(event_type: 'standard')
|
||||||
|
end
|
||||||
|
end
|
@ -1243,7 +1243,7 @@ CREATE TABLE public.events (
|
|||||||
age_range_id integer,
|
age_range_id integer,
|
||||||
category_id integer,
|
category_id integer,
|
||||||
deleted_at timestamp without time zone,
|
deleted_at timestamp without time zone,
|
||||||
booking_nominative boolean DEFAULT false
|
event_type character varying DEFAULT 'standard'::character varying
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -8927,6 +8927,8 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||||||
('20230331132506'),
|
('20230331132506'),
|
||||||
('20230509121907'),
|
('20230509121907'),
|
||||||
('20230509161557'),
|
('20230509161557'),
|
||||||
('20230510141305');
|
('20230510141305'),
|
||||||
|
('20230511080650'),
|
||||||
|
('20230511081018');
|
||||||
|
|
||||||
|
|
||||||
|
1
test/fixtures/history_values.yml
vendored
1
test/fixtures/history_values.yml
vendored
@ -858,5 +858,4 @@ history_value_101:
|
|||||||
value: 'false'
|
value: 'false'
|
||||||
created_at: '2023-03-31 14:38:40.000421'
|
created_at: '2023-03-31 14:38:40.000421'
|
||||||
updated_at: '2023-03-31 14:38:40.000421'
|
updated_at: '2023-03-31 14:38:40.000421'
|
||||||
footprint:
|
|
||||||
invoicing_profile_id: 1
|
invoicing_profile_id: 1
|
||||||
|
@ -15,6 +15,7 @@ describe('EventForm', () => {
|
|||||||
expect(screen.getByLabelText(/app.admin.event_form.title/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.title/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.matching_visual/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.matching_visual/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.description/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.description/)).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText(/app.admin.event_form.event_type/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.event_category/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.event_category/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.event_themes/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.event_themes/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.age_range/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.age_range/)).toBeInTheDocument();
|
||||||
@ -27,7 +28,6 @@ describe('EventForm', () => {
|
|||||||
expect(screen.getByLabelText(/app.admin.event_form._and_ends_on/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form._and_ends_on/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.seats_available/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.seats_available/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.standard_rate/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.event_form.standard_rate/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.event_form.booking_nominative/)).toBeInTheDocument();
|
|
||||||
expect(screen.getByRole('button', { name: /app.admin.event_form.add_price/ })).toBeInTheDocument();
|
expect(screen.getByRole('button', { name: /app.admin.event_form.add_price/ })).toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: /app.admin.event_form.add_a_new_file/ })).toBeInTheDocument();
|
expect(screen.getByRole('button', { name: /app.admin.event_form.add_a_new_file/ })).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.advanced_accounting_form.code/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.advanced_accounting_form.code/)).toBeInTheDocument();
|
||||||
|
@ -22,6 +22,7 @@ class Events::AsAdminTest < ActionDispatch::IntegrationTest
|
|||||||
end_date: 1.week.from_now.utc,
|
end_date: 1.week.from_now.utc,
|
||||||
end_time: 1.week.from_now.utc.change(hour: 20),
|
end_time: 1.week.from_now.utc.change(hour: 20),
|
||||||
category_id: Category.first.id,
|
category_id: Category.first.id,
|
||||||
|
event_type: 'standard',
|
||||||
amount: 0
|
amount: 0
|
||||||
}
|
}
|
||||||
}.to_json,
|
}.to_json,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user