mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-28 09:24:24 +01:00
(wip) add/edit user children
This commit is contained in:
parent
7d10132953
commit
5365cbdaba
52
app/controllers/api/children_controller.rb
Normal file
52
app/controllers/api/children_controller.rb
Normal file
@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# API Controller for resources of type Child
|
||||
# Children are used to provide a way to manage multiple users in the family account
|
||||
class API::ChildrenController < API::ApiController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_child, only: %i[show update destroy]
|
||||
|
||||
def index
|
||||
@children = policy_scope(Child)
|
||||
end
|
||||
|
||||
def show
|
||||
authorize @child
|
||||
end
|
||||
|
||||
def create
|
||||
@child = Child.new(child_params)
|
||||
authorize @child
|
||||
if @child.save
|
||||
render status: :created
|
||||
else
|
||||
render json: @child.errors.full_messages, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @child
|
||||
|
||||
if @child.update(child_params)
|
||||
render status: :ok
|
||||
else
|
||||
render json: @child.errors.full_messages, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @child
|
||||
@child.destroy
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_child
|
||||
@child = Child.find(params[:id])
|
||||
end
|
||||
|
||||
def child_params
|
||||
params.require(:child).permit(:first_name, :last_name, :email, :phone, :birthday, :user_id)
|
||||
end
|
||||
end
|
31
app/frontend/src/javascript/api/child.ts
Normal file
31
app/frontend/src/javascript/api/child.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import apiClient from './clients/api-client';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { Child, ChildIndexFilter } from '../models/child';
|
||||
import ApiLib from '../lib/api';
|
||||
|
||||
export default class ChildAPI {
|
||||
static async index (filters: ChildIndexFilter): Promise<Array<Child>> {
|
||||
const res: AxiosResponse<Array<Child>> = await apiClient.get(`/api/children${ApiLib.filtersToQuery(filters)}`);
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async get (id: number): Promise<Child> {
|
||||
const res: AxiosResponse<Child> = await apiClient.get(`/api/children/${id}`);
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async create (child: Child): Promise<Child> {
|
||||
const res: AxiosResponse<Child> = await apiClient.post('/api/children', { child });
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async update (child: Child): Promise<Child> {
|
||||
const res: AxiosResponse<Child> = await apiClient.patch(`/api/children/${child.id}`, { child });
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async destroy (childId: number): Promise<void> {
|
||||
const res: AxiosResponse<void> = await apiClient.delete(`/api/children/${childId}`);
|
||||
return res?.data;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Child } from '../../models/child';
|
||||
import { TDateISODate } from '../../typings/date-iso';
|
||||
import { FormInput } from '../form/form-input';
|
||||
|
||||
interface ChildFormProps {
|
||||
child: Child;
|
||||
onChange: (field: string, value: string | TDateISODate) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A form for creating or editing a child.
|
||||
*/
|
||||
export const ChildForm: React.FC<ChildFormProps> = ({ child, onChange }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
const { register, formState } = useForm<Child>({
|
||||
defaultValues: child
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle the change of a child form field
|
||||
*/
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
onChange(event.target.id, event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="child-form">
|
||||
<div className="info-area">
|
||||
{t('app.public.child_form.child_form_info')}
|
||||
</div>
|
||||
<form>
|
||||
<FormInput id="first_name"
|
||||
register={register}
|
||||
rules={{ required: true }}
|
||||
formState={formState}
|
||||
label={t('app.public.child_form.first_name')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormInput id="last_name"
|
||||
register={register}
|
||||
rules={{ required: true }}
|
||||
formState={formState}
|
||||
label={t('app.public.child_form.last_name')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { Child } from '../../models/child';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
|
||||
interface ChildItemProps {
|
||||
child: Child;
|
||||
onEdit: (child: Child) => void;
|
||||
onDelete: (child: Child) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A child item.
|
||||
*/
|
||||
export const ChildItem: React.FC<ChildItemProps> = ({ child, onEdit, onDelete }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
return (
|
||||
<div className="child-item">
|
||||
<div>
|
||||
<div>{t('app.public.child_item.last_name')}</div>
|
||||
<div>{child.last_name}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>{t('app.public.child_item.first_name')}</div>
|
||||
<div>{child.first_name}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>{t('app.public.child_item.birthday')}</div>
|
||||
<div>{child.birthday}</div>
|
||||
</div>
|
||||
<div className="actions">
|
||||
<FabButton icon={<i className="fa fa-edit" />} onClick={() => onEdit(child)} className="edit-button" />
|
||||
<FabButton icon={<i className="fa fa-trash" />} onClick={() => onDelete(child)} className="delete-button" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,69 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabModal, ModalSize } from '../base/fab-modal';
|
||||
import { Child } from '../../models/child';
|
||||
import { TDateISODate } from '../../typings/date-iso';
|
||||
import ChildAPI from '../../api/child';
|
||||
import { ChildForm } from './child-form';
|
||||
|
||||
interface ChildModalProps {
|
||||
child?: Child;
|
||||
isOpen: boolean;
|
||||
toggleModal: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A modal for creating or editing a child.
|
||||
*/
|
||||
export const ChildModal: React.FC<ChildModalProps> = ({ child, isOpen, toggleModal }) => {
|
||||
const { t } = useTranslation('public');
|
||||
const [data, setData] = useState<Child>(child);
|
||||
console.log(child, data);
|
||||
|
||||
/**
|
||||
* Save the child to the API
|
||||
*/
|
||||
const handleSaveChild = async (): Promise<void> => {
|
||||
try {
|
||||
if (child?.id) {
|
||||
await ChildAPI.update(data);
|
||||
} else {
|
||||
await ChildAPI.create(data);
|
||||
}
|
||||
toggleModal();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the form is valid to save the child
|
||||
*/
|
||||
const isPreventedSaveChild = (): boolean => {
|
||||
return !data?.first_name || !data?.last_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the change of a child form field
|
||||
*/
|
||||
const handleChildChanged = (field: string, value: string | TDateISODate): void => {
|
||||
setData({
|
||||
...data,
|
||||
[field]: value
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<FabModal title={t(`app.public.child_modal.${child?.id ? 'edit' : 'new'}_child`)}
|
||||
width={ModalSize.large}
|
||||
isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
closeButton={true}
|
||||
confirmButton={t('app.public.child_modal.save')}
|
||||
onConfirm={handleSaveChild}
|
||||
preventConfirm={isPreventedSaveChild()}>
|
||||
<ChildForm child={child} onChange={handleChildChanged} />
|
||||
</FabModal>
|
||||
);
|
||||
};
|
@ -0,0 +1,86 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { Child } from '../../models/child';
|
||||
// import { ChildListItem } from './child-list-item';
|
||||
import ChildAPI from '../../api/child';
|
||||
import { User } from '../../models/user';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Loader } from '../base/loader';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { ChildModal } from './child-modal';
|
||||
import { ChildItem } from './child-item';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface ChildrenListProps {
|
||||
currentUser: User;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of children belonging to the current user.
|
||||
*/
|
||||
export const ChildrenList: React.FC<ChildrenListProps> = ({ currentUser }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
const [children, setChildren] = useState<Array<Child>>([]);
|
||||
const [isOpenChildModal, setIsOpenChildModal] = useState<boolean>(false);
|
||||
const [child, setChild] = useState<Child>();
|
||||
|
||||
useEffect(() => {
|
||||
ChildAPI.index({ user_id: currentUser.id }).then(setChildren);
|
||||
}, [currentUser]);
|
||||
|
||||
/**
|
||||
* Open the add child modal
|
||||
*/
|
||||
const addChild = () => {
|
||||
setIsOpenChildModal(true);
|
||||
setChild({ user_id: currentUser.id } as Child);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the edit child modal
|
||||
*/
|
||||
const editChild = (child: Child) => {
|
||||
setIsOpenChildModal(true);
|
||||
setChild(child);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a child
|
||||
*/
|
||||
const deleteChild = (child: Child) => {
|
||||
ChildAPI.destroy(child.id).then(() => {
|
||||
setChildren(children.filter(c => c.id !== child.id));
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<header>
|
||||
<h2>{t('app.public.children_list.heading')}</h2>
|
||||
<FabButton onClick={addChild}>
|
||||
{t('app.public.children_list.add_child')}
|
||||
</FabButton>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
{children.map(child => (
|
||||
<ChildItem key={child.id} child={child} onEdit={editChild} onDelete={deleteChild} />
|
||||
))}
|
||||
</div>
|
||||
<ChildModal child={child} isOpen={isOpenChildModal} toggleModal={() => setIsOpenChildModal(false)} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const ChildrenListWrapper: React.FC<ChildrenListProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ChildrenList {...props} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('childrenList', react2angular(ChildrenListWrapper, ['currentUser']));
|
23
app/frontend/src/javascript/controllers/children.js
Normal file
23
app/frontend/src/javascript/controllers/children.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
Application.Controllers.controller('ChildrenController', ['$scope', 'memberPromise', 'growl',
|
||||
function ($scope, memberPromise, growl) {
|
||||
// Current user's profile
|
||||
$scope.user = memberPromise;
|
||||
|
||||
/**
|
||||
* Callback used to display a error message
|
||||
*/
|
||||
$scope.onError = function (message) {
|
||||
console.error(message);
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback used to display a success message
|
||||
*/
|
||||
$scope.onSuccess = function (message) {
|
||||
growl.success(message);
|
||||
};
|
||||
}
|
||||
]);
|
16
app/frontend/src/javascript/models/child.ts
Normal file
16
app/frontend/src/javascript/models/child.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TDateISODate } from '../typings/date-iso';
|
||||
import { ApiFilter } from './api';
|
||||
|
||||
export interface ChildIndexFilter extends ApiFilter {
|
||||
user_id: number,
|
||||
}
|
||||
|
||||
export interface Child {
|
||||
id?: number,
|
||||
last_name: string,
|
||||
first_name: string,
|
||||
email?: string,
|
||||
phone?: string,
|
||||
birthday: TDateISODate,
|
||||
user_id: number
|
||||
}
|
@ -28,9 +28,9 @@ angular.module('application.router', ['ui.router'])
|
||||
logoBlackFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-black-file' }).$promise; }],
|
||||
sharedTranslations: ['Translations', function (Translations) { return Translations.query(['app.shared', 'app.public.common']).$promise; }],
|
||||
modulesPromise: ['Setting', function (Setting) { return Setting.query({ names: "['machines_module', 'spaces_module', 'plans_module', 'invoicing_module', 'wallet_module', 'statistics_module', 'trainings_module', 'public_agenda_module', 'store_module']" }).$promise; }],
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['public_registrations', 'store_hidden']" }).$promise; }]
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['public_registrations', 'store_hidden', 'family_account']" }).$promise; }]
|
||||
},
|
||||
onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', 'modulesPromise', 'CSRF', function ($rootScope, logoFile, logoBlackFile, modulesPromise, CSRF) {
|
||||
onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', 'modulesPromise', 'settingsPromise', 'CSRF', function ($rootScope, logoFile, logoBlackFile, modulesPromise, settingsPromise, CSRF) {
|
||||
// Retrieve Anti-CSRF tokens from cookies
|
||||
CSRF.setMetaTags();
|
||||
// Application logo
|
||||
@ -47,6 +47,9 @@ angular.module('application.router', ['ui.router'])
|
||||
publicAgenda: (modulesPromise.public_agenda_module === 'true'),
|
||||
statistics: (modulesPromise.statistics_module === 'true')
|
||||
};
|
||||
$rootScope.settings = {
|
||||
familyAccount: (settingsPromise.family_account === 'true')
|
||||
};
|
||||
}]
|
||||
})
|
||||
.state('app.public', {
|
||||
@ -151,6 +154,15 @@ angular.module('application.router', ['ui.router'])
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('app.logged.dashboard.children', {
|
||||
url: '/children',
|
||||
views: {
|
||||
'main@': {
|
||||
templateUrl: '/dashboard/children.html',
|
||||
controller: 'ChildrenController'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('app.logged.dashboard.settings', {
|
||||
url: '/settings',
|
||||
views: {
|
||||
|
11
app/frontend/templates/dashboard/children.html
Normal file
11
app/frontend/templates/dashboard/children.html
Normal file
@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<section class="heading">
|
||||
<div class="row no-gutter">
|
||||
<ng-include src="'/dashboard/nav.html'"></ng-include>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<children-list current-user="currentUser" on-success="onSuccess" on-error="onError" />
|
||||
</div>
|
@ -11,6 +11,7 @@
|
||||
<h4 class="m-l text-sm" translate>{{ 'app.public.common.dashboard' }}</h4>
|
||||
<ul class="nav-page nav nav-pills text-u-c text-sm">
|
||||
<li ui-sref-active="active"><a class="text-black" ui-sref="app.logged.dashboard.profile" translate>{{ 'app.public.common.my_profile' }}</a></li>
|
||||
<li ng-show="$root.settings.familyAccount" ui-sref-active="active"><a class="text-black" ui-sref="app.logged.dashboard.children" translate>{{ 'app.public.common.my_children' }}</a></li>
|
||||
<li ui-sref-active="active"><a class="text-black" ui-sref="app.logged.dashboard.settings" translate>{{ 'app.public.common.my_settings' }}</a></li>
|
||||
<li ng-if="!isAuthorized(['admin', 'manager']) && hasProofOfIdentityTypes" ui-sref-active="active"><a class="text-black" ui-sref="app.logged.dashboard.supporting_document_files" translate>{{ 'app.public.common.my_supporting_documents_files' }}</a></li>
|
||||
<li ui-sref-active="active"><a class="text-black" ui-sref="app.logged.dashboard.projects" translate>{{ 'app.public.common.my_projects' }}</a></li>
|
||||
|
14
app/models/child.rb
Normal file
14
app/models/child.rb
Normal file
@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Child is a modal for a child of a user
|
||||
class Child < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
validates :first_name, presence: true
|
||||
validates :last_name, presence: true
|
||||
validate :validate_age
|
||||
|
||||
def validate_age
|
||||
errors.add(:birthday, 'You should be over 18 years old.') if birthday.blank? && birthday < 18.years.ago
|
||||
end
|
||||
end
|
@ -53,6 +53,9 @@ class User < ApplicationRecord
|
||||
has_many :notifications, as: :receiver, dependent: :destroy
|
||||
has_many :notification_preferences, dependent: :destroy
|
||||
|
||||
has_many :children, dependent: :destroy
|
||||
accepts_nested_attributes_for :children, allow_destroy: true
|
||||
|
||||
# fix for create admin user
|
||||
before_save do
|
||||
email&.downcase!
|
||||
|
31
app/policies/child_policy.rb
Normal file
31
app/policies/child_policy.rb
Normal file
@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check the access policies for API::ChildrenController
|
||||
class ChildPolicy < ApplicationPolicy
|
||||
# Defines the scope of the children index, depending on the current user
|
||||
class Scope < Scope
|
||||
def resolve
|
||||
scope.where(user_id: user.id)
|
||||
end
|
||||
end
|
||||
|
||||
def index?
|
||||
!user.organization?
|
||||
end
|
||||
|
||||
def create?
|
||||
!user.organization? && user.id == record.user_id
|
||||
end
|
||||
|
||||
def show?
|
||||
user.id == record.user_id
|
||||
end
|
||||
|
||||
def update?
|
||||
user.id == record.user_id
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.id == record.user_id
|
||||
end
|
||||
end
|
3
app/views/api/children/_child.json.jbuilder
Normal file
3
app/views/api/children/_child.json.jbuilder
Normal file
@ -0,0 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! child, :id, :first_name, :last_name, :email, :birthday, :phone, :user_id
|
3
app/views/api/children/create.json.jbuilder
Normal file
3
app/views/api/children/create.json.jbuilder
Normal file
@ -0,0 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.partial! 'child', child: @child
|
5
app/views/api/children/index.json.jbuilder
Normal file
5
app/views/api/children/index.json.jbuilder
Normal file
@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.array! @children do |child|
|
||||
json.partial! 'child', child: child
|
||||
end
|
3
app/views/api/children/update.json.jbuilder
Normal file
3
app/views/api/children/update.json.jbuilder
Normal file
@ -0,0 +1,3 @@
|
||||
# forzen_string_literal: true
|
||||
|
||||
json.partial! 'child', child: @child
|
@ -14,6 +14,7 @@ en:
|
||||
#dashboard sections
|
||||
dashboard: "Dashboard"
|
||||
my_profile: "My Profile"
|
||||
my_children: "My Children"
|
||||
my_settings: "My Settings"
|
||||
my_supporting_documents_files: "My supporting documents"
|
||||
my_projects: "My Projects"
|
||||
@ -481,6 +482,8 @@ en:
|
||||
member_select:
|
||||
select_a_member: "Select a member"
|
||||
start_typing: "Start typing..."
|
||||
children_list:
|
||||
heading: "My children"
|
||||
tour:
|
||||
conclusion:
|
||||
title: "Thank you for your attention"
|
||||
|
@ -14,6 +14,7 @@ fr:
|
||||
#dashboard sections
|
||||
dashboard: "Tableau de bord"
|
||||
my_profile: "Mon profil"
|
||||
my_children: "Mes enfants"
|
||||
my_settings: "Mes paramètres"
|
||||
my_supporting_documents_files: "Mes justificatifs"
|
||||
my_projects: "Mes projets"
|
||||
@ -481,6 +482,21 @@ fr:
|
||||
member_select:
|
||||
select_a_member: "Sélectionnez un membre"
|
||||
start_typing: "Commencez à écrire..."
|
||||
children_list:
|
||||
heading: "Mes enfants"
|
||||
add_child: "Ajouter un enfant"
|
||||
child_modal:
|
||||
edit_child: "Modifier un enfant"
|
||||
new_child: "Ajouter un enfant"
|
||||
save: "Enregistrer"
|
||||
child_form:
|
||||
child_form_info: "Notez que vous ne pouvez ajouter que vos enfants de moins de 18 ans. Des pièces justificatives sont demandés par votre administrateur, elles lui seront utiles pour valider le compte de votre enfant et ainsi autoriser la réservation d'événements."
|
||||
first_name: "Prénom"
|
||||
last_name: "Nom"
|
||||
child_item:
|
||||
first_name: "Prénom de l'enfant"
|
||||
last_name: "Nom de l'enfant"
|
||||
birthday: "Date de naissance"
|
||||
tour:
|
||||
conclusion:
|
||||
title: "Merci de votre attention"
|
||||
|
@ -185,6 +185,8 @@ Rails.application.routes.draw do
|
||||
get 'withdrawal_instructions', on: :member
|
||||
end
|
||||
|
||||
resources :children, only: %i[index show create update destroy]
|
||||
|
||||
# for admin
|
||||
resources :trainings do
|
||||
get :availabilities, on: :member
|
||||
|
17
db/migrate/20230331132506_create_children.rb
Normal file
17
db/migrate/20230331132506_create_children.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Child is a modal for a child of a user
|
||||
class CreateChildren < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :children do |t|
|
||||
t.belongs_to :user, foreign_key: true
|
||||
t.string :first_name
|
||||
t.string :last_name
|
||||
t.date :birthday
|
||||
t.string :phone
|
||||
t.string :email
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
17
db/schema.rb
17
db/schema.rb
@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[6.1].define(version: 2023_03_15_095054) do
|
||||
ActiveRecord::Schema[6.1].define(version: 2023_03_31_132506) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "fuzzystrmatch"
|
||||
@ -263,6 +263,18 @@ ActiveRecord::Schema[6.1].define(version: 2023_03_15_095054) do
|
||||
t.index ["slug"], name: "index_categories_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "children", force: :cascade do |t|
|
||||
t.bigint "user_id"
|
||||
t.string "first_name"
|
||||
t.string "last_name"
|
||||
t.date "birthday"
|
||||
t.string "phone"
|
||||
t.string "email"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["user_id"], name: "index_children_on_user_id"
|
||||
end
|
||||
|
||||
create_table "components", id: :serial, force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
end
|
||||
@ -1332,8 +1344,8 @@ ActiveRecord::Schema[6.1].define(version: 2023_03_15_095054) do
|
||||
t.boolean "is_allow_newsletter"
|
||||
t.inet "current_sign_in_ip"
|
||||
t.inet "last_sign_in_ip"
|
||||
t.string "mapped_from_sso"
|
||||
t.datetime "validated_at"
|
||||
t.string "mapped_from_sso"
|
||||
t.index ["auth_token"], name: "index_users_on_auth_token"
|
||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
@ -1409,6 +1421,7 @@ ActiveRecord::Schema[6.1].define(version: 2023_03_15_095054) do
|
||||
add_foreign_key "cart_item_reservations", "plans"
|
||||
add_foreign_key "cart_item_subscriptions", "invoicing_profiles", column: "customer_profile_id"
|
||||
add_foreign_key "cart_item_subscriptions", "plans"
|
||||
add_foreign_key "children", "users"
|
||||
add_foreign_key "event_price_categories", "events"
|
||||
add_foreign_key "event_price_categories", "price_categories"
|
||||
add_foreign_key "events", "categories"
|
||||
|
Loading…
Reference in New Issue
Block a user