1
0
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:
Du Peng 2023-05-11 11:22:23 +02:00
parent 6888f00036
commit aed5d1fc1b
16 changed files with 56 additions and 30 deletions

View File

@ -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],

View File

@ -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"

View File

@ -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({

View File

@ -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 {

View File

@ -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">

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

View 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

View File

@ -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');

View File

@ -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

View File

@ -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();

View File

@ -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,