Skip to content

Instantly share code, notes, and snippets.

@trdev7
Created June 8, 2020 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trdev7/5dc75776e7701ef6709934ef460f6169 to your computer and use it in GitHub Desktop.
Save trdev7/5dc75776e7701ef6709934ef460f6169 to your computer and use it in GitHub Desktop.
ProductForm on Bookthatapp
import React, { Component } from 'react';
import FormSelect from '../common/form_select.js.jsx';
import FormInput from '../common/form_input.js.jsx';
import FormNumberInput from '../common/form_number_input.js.jsx';
import FormInputWithTime from './form_input_with_time.js.jsx';
import FormNested from './form_nested.js.jsx';
import LocationInventories from './location_inventories.jsx';
import ProductVariantsForm from './product_variants_form.js.jsx';
import RoomTime from './room_time.js.jsx';
import FormCheckbox from '../common/form_checkbox.js.jsx';
import ProductCapacitiesForm from './product_capacities_form.js.jsx';
import ProductDurationsForm from './product_durations_form.js.jsx';
import ScheduleForm from '../common/schedule_form.js.jsx';
import WidgetPicker from '../common/widget_picker.js.jsx';
import Tooltip from '../common/tooltip.js.jsx';
import ProductTerms from './product_terms.js.jsx';
import ErrorsContainer from './errors_container.js.jsx';
import InputFeeds from './input_feeds.js.jsx';
import Tabs from '../common/tabs';
import Tab from '../common/tab';
import FormDeleteButtonContainer from '../../containers/form_delete_button';
import Utils from '../common/utils.js';
import CopyProductForm from './copy_product_form.js';
import {
PRODUCT_PROFILE,
ACTIVITY_PROFILE,
COURSE_PROFILE,
CLASS_PROFILE,
APPT_PROFILE,
ROOM_PROFILE,
GENERAL_PROFILE,
} from '../../reducers/product.js.jsx';
import ActionsList from '../common/actions_list.js';
import ProductResources from './product_resources.jsx';
require('react-datetime/css/react-datetime.css');
class ProductForm extends React.Component {
constructor(props) {
super(props);
this.handleResyncClick = this.handleResyncClick.bind(this);
}
_validProduct() {
const errors = [];
const inputFeedErrors = {};
const self = this;
if (this.props.product.profile == ROOM_PROFILE) {
if (!this.props.product.check_in_time) {
errors.push('Check In Time is blank. Please set it.');
}
if (!this.props.product.check_out_time) {
errors.push('Check Out Time is blank. Please set it.');
}
}
this.props.product.input_feeds.forEach((feed, index) => {
self._validateInputFeed(feed, index, inputFeedErrors);
});
if (Object.keys(inputFeedErrors).length > 0) {
errors.push('Invalid external calendar url.');
}
this.props.actions.onSetInputFeedsValidationErrors(inputFeedErrors);
this.props.actions.onProductErrorsSet(errors);
if (errors.length > 0) {
Utils.onErrorShow(errors);
return false;
}
return true;
}
_validateInputFeed(feed, index, errors) {
if (feed) {
if (feed._destroy) {
return errors;
}
if (!feed.name) {
this._setInputFeedError(index, 'name', "can't be blank", errors);
}
if (!feed.url) {
this._setInputFeedError(index, 'url', "can't be blank", errors);
}
if (feed.url && !Utils.validURL(feed.url)) {
this._setInputFeedError(index, 'url', "is not a valid URL", errors);
}
return errors;
}
}
_setInputFeedError(index, field, message, errors) {
if (!errors[index]) {
errors[index] = {};
}
errors[index][field] = message;
return errors;
}
onSubmit() {
if (this._validProduct()) {
this.props.actions.onProductSubmit(this.productData());
}
}
productData() {
// build request data with input_feeds_attributes
let feeds = this.props.product.input_feeds.filter(feed => feed);
let data = Object.assign({input_feeds_attributes: feeds}, this.props.product);
delete data.input_feeds;
return data
}
durationEnabled() {
return this.props.product.scheduled || this.props.duration_enabled;
}
getScheduleDuration() {
let duration = this.props.product.duration || 3600;
if (this.props.product.range_count_basis == '0' && duration > 86400) {
duration += 86400;
}
return duration;
}
goToPage(url) {
if (this.props.product.id) {
if (this.props.isModified) {
const r = confirm(
'If you leave this page, all unsaved changes will be lost. Are you sure you want to leave this page?',
);
if (r == true) {
window.location.href = url;
}
} else {
window.location.href = url;
}
} else alert('This product should be saved first');
}
handleResyncClick(e) {
e.preventDefault();
var self = this;
btaa.notify('Synchronization in progress...');
self.props.actions.onProductLoadingSet(true);
fetch(`/admin/products/${this.props.product.id}/resync`, {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin'
}).then(function (response) {
return response.json();
}).then(function(data) {
btaa.notify(data.message);
self.props.actions.onProductLoadingSet(false);
window.location = window.location; // reload
});
}
notificationActions() {
if (Utils.gup('created')) {
const actions = [
{ title: 'Create Another Product', url: '/admin/products/new' },
{
title: 'View Product',
url: `https:${this.props.product.shopify_url}`,
target: '_blank',
icon: 'fa-desktop',
},
{
title: 'Create Product Booking',
url: `/admin/bookings/new?product=${this.props.product.id}`,
icon: 'fa-calendar-plus-o',
},
];
return actions;
}
return [];
}
isCapacityResourceBased() {
return this.props.product.capacity_type === 2;
}
render() {
const { product, multiLocationInventoryEnabled } = this.props;
return (
<div className="form product-container">
<ActionsList title="Product" itemActions={this.notificationActions()} />
<ErrorsContainer message="Could not save product" errors={this.props.productErrors} />
<div className="row">
<div className="col-xs-12 col-md-12">
<div className="ibox">
<div className="ibox-content">
<Tabs className="icon-tabs">
<Tab title="Product">
<div id="product">
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#product-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Product</h3>
<div id="product-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
The product profile determines what types of bookings this product is for. Depending on what
profile is chosen you will see different fields. Booking forms also depend on the profile. For
more information please see our help article "
<a href="//support.zetya.com/hc/en-us/articles/211514406-How-do-I-choose-the-right-booking-form-profile-">
How do I choose the right booking form/profile
</a>
".
</p>
</div>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label>Profile</label>
<FormSelect
name="profile"
value={this.props.product.profile}
options={this.props.profileOptions}
onChange={val => this.props.actions.onProfileSet(val)}
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group" id="product_title">
<label>Product Title</label>
<FormInput
type="text"
name="product_title"
value={this.props.product.product_title}
onChange={this.props.actions.onTitleSet}
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label>Product Handle</label>
<FormInput
type="text"
name="product_handle"
isDisabled
onChange={function () {}}
value={this.props.product.product_handle}
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label>Tags</label>
<FormInput type="text" name="tags" isDisabled value={this.props.product.tag_list.join(', ')} />
</div>
</div>
{Utils.displayForProfiles([GENERAL_PROFILE], product.profile) && (
<div className="col-sm-6">
<div className="form-group m-t">
<label className="m-r">
Scheduled?
<Tooltip title="Select if this product is a one-off or recurring event (e.g. class) that should appear in your store's calendar." />
</label>
<FormCheckbox
name="scheduled"
value={this.props.product.scheduled}
onChange={this.props.actions.onScheduledSet}
/>
</div>
</div>
)}
</div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
{/* === CAPACITY SECTION === */}
{multiLocationInventoryEnabled ? (
<LocationInventories />
) : (
<div>
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#capacity-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Capacity</h3>
<div id="capacity-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
Capacity limits how many bookings can be made at any one time. Product based capacity
counts all bookings regardless of the variant booked. Variant based capacity counts only
bookings for a given variant. Resource based capacity limits the bookings to the
availability of the resources assigned to the product.
</p>
<p>
For more information, refer to our
{' '}
<a
href="//support.zetya.com/hc/en-us/articles/211514426-Setting-Capacity"
target="_blank"
>
help desk article
</a>
.
</p>
</div>
</div>
</div>
<div id="capacity">
<ProductCapacitiesForm
profile={this.props.product.profile}
capacities={this.props.product.option_capacities_attributes}
capacity={this.props.product.capacity}
capacity_option1={this.props.product.capacity_option1}
capacity_option2={this.props.product.capacity_option2}
capacity_option3={this.props.product.capacity_option3}
configurationOptions={this.props.configurationOptions}
capacityType={this.props.product.capacity_type}
onCapacityOptionEnable={this.props.actions.onCapacityOptionEnable}
onCapacitySet={this.props.actions.onCapacitySet}
onCapacityTypeSet={this.props.actions.onCapacityTypeSet}
onOptionCapacitiesSet={this.props.actions.onOptionCapacitiesSet}
onOptionCapacitySet={this.props.actions.onOptionCapacitySet}
variantOptions={this.props.variantOptions}
capacitiesOptions={this.props.capacityOptions}
/>
</div>
</div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
</div>
)}
{/* === DURATION SECTION === */}
{Utils.displayForProfiles(
[PRODUCT_PROFILE, ACTIVITY_PROFILE, CLASS_PROFILE, APPT_PROFILE, GENERAL_PROFILE, COURSE_PROFILE],
product.profile,
)
&& this.durationEnabled() && (
<div>
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#duration-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Duration</h3>
<div id="duration-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
Determines how the end date for bookings is calculated. If the duration is the same
across all variants set the Duration Basis to Product. If the duration changes depending
on what variant is selected, or if you are using a date range and want to change the
rate depending on how many days are selected, choose Variant Option.
</p>
</div>
</div>
</div>
</div>
<div className="row">
<div id="duration">
<ProductDurationsForm
profile={this.props.product.profile}
duration={this.props.product.duration || 3600}
allDay={this.props.product.all_day}
durationOption={this.props.product.duration_option}
durationOptionRangeVariant={this.props.product.duration_option_range_variant}
durationOptions={this.props.durationOptions}
onDurationSet={this.props.actions.onDurationSet}
onProductAllDaySet={this.props.actions.onProductAllDaySet}
onDurationTypeSet={this.props.actions.onDurationTypeSet}
onDurationOptionRangeVariantSet={this.props.actions.onDurationOptionRangeVariantSet}
onDurationOptionSet={this.props.actions.onDurationOptionSet}
activityDurationVariantsEnabled={this.props.activity_duration_variants_enabled}
durationEnabled={this.props.duration_enabled}
durationType={this.props.product.duration_type}
variantOptions={this.props.variantOptions}
optionDurations={this.props.product.option_durations_attributes}
onOptionDurationSet={this.props.actions.onOptionDurationSet}
onOptionDurationsSet={this.props.actions.onOptionDurationsSet}
/>
</div>
</div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
</div>
)}
{/* === ROOM SECTION === */}
{Utils.displayForProfiles([ROOM_PROFILE], product.profile) && (
<RoomTime
checkIn={this.props.product.check_in_time}
checkOut={this.props.product.check_out_time}
changeCheckInTime={val => this.props.actions.onCheckInTimeSet(val)}
changeCheckOutTime={val => this.props.actions.onCheckOutTimeSet(val)}
/>
)}
{/* === LEAD/LAG TIME & DATEPICKER SECTIONS === */}
{Utils.displayForProfiles(
[PRODUCT_PROFILE, ACTIVITY_PROFILE, APPT_PROFILE, ROOM_PROFILE, GENERAL_PROFILE, CLASS_PROFILE],
product.profile,
) && (
<div className="row">
{Utils.displayForProfiles(
[PRODUCT_PROFILE, ACTIVITY_PROFILE, APPT_PROFILE, GENERAL_PROFILE],
product.profile,
) && (
<div className="col-md-6">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#leadlag-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Lead/Lag Times</h3>
<div id="leadlag-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
<strong>Lead Time</strong>
: Number of days to allow before the next booking can begin, for
example to account for delivery time. Unlike lag time, the lead time component is not
included when a booking is created from an order.
</p>
<p>
<strong>Lag Time</strong>
: Lag time is automatically added to the booking finish time on
creation. Typically this is used to account for the time required to get an item ready
before the next booking.
</p>
</div>
</div>
<div className="row">
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="lead_time">Lead Time (Days)</label>
<FormNumberInput
id="lead_time"
name="lead_time"
value={this.props.product.lead_time}
min={0}
onChange={this.props.actions.onLeadTimeSet}
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group" id="lag_time">
<label htmlFor="lag_time">Lag Time</label>
<FormInputWithTime
id="lag_time"
name="lag_time"
value={this.props.product.lag_time}
onChange={this.props.actions.onLagTimeSet}
/>
</div>
</div>
</div>
</div>
)}
{Utils.displayForProfiles(
[PRODUCT_PROFILE, ROOM_PROFILE, GENERAL_PROFILE, CLASS_PROFILE, ACTIVITY_PROFILE],
product.profile,
) && (
<div className="col-md-6">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#range-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Date Range</h3>
<div id="range-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>Specify the minimun and maximun number for the date range.</p>
{Utils.displayForProfiles([PRODUCT_PROFILE, GENERAL_PROFILE], product.profile) && (
<p>
Count is used to specify if days or nights are used when calculating quantity for the
'Date Range Updates Quantity Setting'.
</p>
)}
</div>
</div>
{Utils.displayForProfiles(
[PRODUCT_PROFILE, ROOM_PROFILE, GENERAL_PROFILE],
product.profile,
) && (
<div className="row">
<div className="col-sm-6">
<div className="form-group col-md-3 no-padding">
<label htmlFor="min_duration">Minimum</label>
<FormNumberInput
id="min_duration"
name="min_duration"
value={this.props.product.min_duration}
onChange={this.props.actions.onMinDurationSet}
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group col-md-3 no-padding">
<label htmlFor="max_duration">Maximum</label>
<FormNumberInput
id="max_duration"
name="max_duration"
value={this.props.product.max_duration}
onChange={this.props.actions.onMaxDurationSet}
/>
</div>
</div>
</div>
)}
{Utils.displayForProfiles(
[PRODUCT_PROFILE, GENERAL_PROFILE, CLASS_PROFILE, ACTIVITY_PROFILE],
product.profile,
) && (
<div className="row">
<div className="col-sm-6">
<div className="form-group no-padding">
<label>Count</label>
<FormSelect
value={this.props.product.range_count_basis}
options={this.props.rangeCountBasisOptions}
onChange={this.props.actions.onRangeCountBasisSet}
/>
</div>
</div>
</div>
)}
</div>
)}
<div className="col-sm-12">
<div className="hr-line-dashed m-t-sm m-b-sm" />
</div>
</div>
)}
{/* === VARIANTS SECTION === */}
{Utils.hideForProfiles([COURSE_PROFILE], product.profile) && (
<div>
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#variants-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Variants</h3>
<div id="variants-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
Lists the product variants. Selecting the Hide checkbox will hide the booking form when
the variant is chosen on the product page.
</p>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<ProductVariantsForm
profile={this.props.product.profile}
variants={this.props.product.variants_attributes}
isNew={!this.props.product.id}
legacyVariantTimes={this.props.legacy_variant_times}
onVariantsSet={this.props.actions.onVariantsSet}
// durationEnabled={this.props.duration_enabled}
durationEnabled={!this.durationEnabled()}
scheduled={this.props.product.scheduled}
durationUnitsOptions={this.props.durationUnitOptions}
/>
</div>
</div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
</div>
)}
{/* === RESOURCES & LOCATIONS SECTION === */}
<div className="row">
<div className="col-sm-6">
<h3 className="m-t-none m-b dib">Resources</h3>
<div className="help-icon visible-xs">
<a className="btn" role="button" data-toggle="collapse" href="#locations-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
</div>
<div className="col-sm-6 hidden-xs">
<h3 className="m-t-none m-b dib">Locations</h3>
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#locations-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div id="locations-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
<strong>Resources:</strong> Assign resources associated with this product. When the
capacity type is based on Product or Variants these resources will be added to the
booking automatically when it is created but their bookings do not count towards
availability. If the capacity is based on resources then any bookings for the resource
count towards availability.
</p>
<p>
<strong>Locations:</strong> Choose the locations this product applies to. Location
information such as address, map & email can be used in reminders. Locations are
created via the configuration menu.
</p>
</div>
</div>
</div>
<div className="col-sm-6">
{this.isCapacityResourceBased() && this.props.resourceQueryBuilderEnabled ? (
<ProductResources
resourceOptions={this.props.resourceOptions}
query={this.props.product.resource_capacity_json}
onChange={this.props.actions.onResourcesSet}
/>
) : (
<FormNested
name={this.props.resourceQueryBuilderEnabled ? "resource_capacity_json" : "resource_constraints_attributes"}
placeholder="Choose a Resource"
values={this.props.product.resource_constraints_attributes}
onChange={e => this.props.actions.onResourcesSet(e, this.isCapacityResourceBased())}
options={this.props.resourceOptions}
nestedName="resource_id"
/>
)}
</div>
<div className="hr-line-dashed m-t-sm m-b-sm visible-xs" />
<div className="col-sm-6">
<h3 className="m-t-none m-b visible-xs">Locations</h3>
<FormNested
name="product_locations_attributes"
placeholder="Choose a Location"
values={this.props.product.product_locations_attributes}
onChange={this.props.actions.onLocationsSet}
options={this.props.locationOptions}
availableOptions={this.props.multiLocationInventoryEnabled ? [] : this.props.availableLocations}
nestedName="location_id"
/>
</div>
</div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
{/* === DATEPICKER RESTRICTIONS SECTION === */}
{Utils.hideForProfiles([COURSE_PROFILE], product.profile) && (
<div>
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a
className="btn"
role="button"
data-toggle="collapse"
href="#datepicker-restrictions-help"
title="Help"
>
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">Datepicker</h3>
<div
id="datepicker-restrictions-help"
className="panel panel-info collapse"
aria-expanded="false"
>
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
<strong>Cutoff Days</strong>
: First day (from today) that will be available in the
datepicker. Set to 0 for today, 1 for tomorrow etc.
</p>
<p>
<strong>Future Days</strong>
: Maximum number of days to allow from today. Must be greater
than or equal to Cutoff Days.
</p>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-6">
<div className="no-padding">
<label>Cutoff Days</label>
<FormInputWithTime
name="mindate"
value={this.props.product.mindate}
inputTimeInSec
outputTimeInSec
onChange={this.props.actions.onMindateSet}
/>
</div>
</div>
<div className="col-sm-6">
<div className="col-md-3 no-padding">
<label>Future Days</label>
<FormNumberInput
name="maxdate"
value={this.props.product.maxdate}
onChange={this.props.actions.onMaxdateSet}
/>
</div>
</div>
</div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
</div>
)}
{/* === CALENDAR SETTINGS SECTION === */}
<div className="row">
<div className="col-sm-12">
<h3 className="m-t-none m-b">Calendar Settings</h3>
</div>
</div>
<div className="row">
<div className="col-sm-3">
<div className="form-group">
<label>Text Color</label>
<FormSelect
value={this.props.product.text_color}
options={this.props.colorOptions}
includeBlank
capitalize
onChange={this.props.actions.onTextColorSet}
/>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Background Color</label>
<FormSelect
value={this.props.product.background_color}
options={this.props.colorOptions}
includeBlank
capitalize
onChange={this.props.actions.onBackgroundColorSet}
/>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Border Color</label>
<FormSelect
value={this.props.product.border_color}
options={this.props.colorOptions}
includeBlank
capitalize
onChange={this.props.actions.onBorderColorSet}
/>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Preview</label>
<div>
<p
style={{
borderColor: this.props.product.border_color,
color: this.props.product.text_color,
backgroundColor: this.props.product.background_color,
}}
className="p-xxs border-left-right border-top-bottom"
>
1 pm - 2 pm
{' '}
{this.props.product.product_title}
</p>
</div>
</div>
</div>
</div>
{Utils.displayForProfiles([ACTIVITY_PROFILE, CLASS_PROFILE], product.profile) && (
<div className="row">
<div className="col-sm-12">
<div className="form-group">
<div className="checkbox checkbox-inline">
<FormCheckbox
value={this.props.product.visible_in_calendar}
id="visible_in_calendar"
name="visible_in_calendar"
onChange={this.props.actions.onVisibleInCalendarSet}
/>
<label htmlFor="visible_in_calendar">Show in Store Calendar?</label>
</div>
</div>
</div>
</div>
)}
{/*= == EMAIL NOTIFICATION SECTION === */}
{Utils.displayForProfiles([ACTIVITY_PROFILE, CLASS_PROFILE], product.profile) && (
<div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a
className="btn"
role="button"
data-toggle="collapse"
href="#enrollment-email-help"
title="Help"
>
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">{I18n.t('products.enrollment.email.title')}</h3>
<div id="enrollment-email-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>{I18n.t('products.enrollment.email.help')}</p>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="form-group">
<label>{I18n.t('products.enrollment.email.label')}</label>
<FormInput
name="headsup_email_addresses"
value={this.props.product.headsup_email_addresses}
onChange={val => this.props.actions.onProductChange('headsup_email_addresses', val)}
/>
</div>
</div>
</div>
</div>
)}
{/* === SCHEDULE SECTION === */}
{Utils.displayForProfiles([ACTIVITY_PROFILE, CLASS_PROFILE, GENERAL_PROFILE], product.profile)
&& this.props.product.scheduled && (
<div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
<ScheduleForm
title=""
allDay={this.props.product.all_day}
schedule={this.props.product.schedule_attributes}
onScheduleUpdateByIndex={this.props.actions.onProductScheduleUpdateByIndex}
onNewScheduleAdd={this.props.actions.onNewProductScheduleAdd}
locations={this.props.locationOptions}
eventColors={{
textColor: this.props.product.text_color,
backgroundColor: this.props.product.background_color,
borderColor: this.props.product.border_color,
}}
blackoutsSettings={{ display: true, product_id: this.props.product.id }}
openingHoursSettings={{ display: true, product_id: this.props.product.id }}
duration={this.getScheduleDuration()}
productLocations={this.props.product.product_locations_attributes}
/>
</div>
)}
{/* === COURSE SECTION === */}
{Utils.displayForProfiles([COURSE_PROFILE], product.profile)
&& this.props.product.scheduled && (
<div>
<div className="hr-line-dashed m-t-sm m-b-sm" />
<ProductTerms
items={this.props.product.terms_attributes}
onTermAdd={this.props.actions.onTermAdd}
onTermChange={this.props.actions.onTermSet}
productId={this.props.product.id}
goToPage={this.goToPage.bind(this)}
termEvents={this.props.term_events}
/>
</div>
)}
</div>
</Tab>
<Tab title="Settings">
<div id="settings">
<div className="row">
<div className="col-sm-12">
<div className="row">
<div className="col-sm-12">
<div className="help-icon">
<a className="btn" role="button" data-toggle="collapse" href="#external-calendar-help" title="Help">
<i className="fa fa-info-circle" />
</a>
</div>
<h3 className="m-t-none m-b">External Calendars</h3>
<div id="external-calendar-help" className="panel panel-info collapse" aria-expanded="false">
<div className="panel-heading">
<i className="fa fa-info-circle m-r-xs" />
Help
</div>
<div className="panel-body">
<p>
You can synchronize bookings from other calendars that support exporting to ical.
Just enter the ical urls corresponding to each calendar that the product syncs with.
Times that are busy
in the other calendar will count as busy time towards this product. BookThatApp will
check every hour for updates.
</p>
<p><strong>Google Calendar</strong>: To sync a Google Calendar please see the 'See
your calendar (view only)' section
in <a href='//support.google.com/calendar/answer/37648?hl=en' target='blank'>this
support article</a>.</p>
<p><strong>Facebook Events</strong>: Please see <a
href='//www.facebook.com/help/work/897976446932009' target='blank'>How do I export
my events</a>.</p>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<InputFeeds
inputFeeds={this.props.product.input_feeds}
productId={this.props.product.id}
inputFeedsValidationErrors={this.props.inputFeedsValidationErrors}
onAddInputFeed={this.props.actions.onAddInputFeed}
onDeleteInputFeed={this.props.actions.onDeleteInputFeed}
onSetInputFeedName={this.props.actions.onSetInputFeedName}
onSetInputFeedUrl={this.props.actions.onSetInputFeedUrl}
onImportInputFeed={this.props.actions.onImportInputFeed}
/>
</div>
</div>
</div>
</div>
</div>
</Tab>
</Tabs>
</div>
{' '}
{/* end ibox-content */}
<div className="ibox-footer">
<button
className="btn btn-sm btn-primary"
type="submit"
ref="saveButton"
onClick={this.onSubmit.bind(this)}
disabled={this.props.productIsLoading}
>
Save
</button>
<a href="/admin/products" className="btn btn-sm btn-white">
Close
</a>
{window.bta.isAdmin && this.props.product.id && (
<button
type="submit"
disabled={this.props.productIsLoading}
onClick={this.handleResyncClick}
className="btn btn-sm label-warning">
Resync
</button>
)}
<FormDeleteButtonContainer
id={this.props.product.id}
itemLabel="product"
redirectUrl="/admin/products"
url={`/admin/products/${this.props.product.id}`}
/>
</div>
</div>
</div>
</div>
<CopyProductForm
productId={this.props.product.external_id}
onProductPropertiesCopy={this.props.actions.onProductPropertiesCopy}
onSubmit={this.onSubmit.bind(this)}
/>
</div>
);
}
}
export default ProductForm;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment