Commit 4f2e594f authored by Salgado M.V.I.'s avatar Salgado M.V.I.

Merge branch 'scripting-feature' into 'master'

scripting feature

See merge request !3
parents 0cb5a98e 2a41e78a
## Compiled JavaScript files
**/*.js
**/*.js.map
# Typescript v1 declaration files
typings/
node_modules
dist
\ No newline at end of file
{
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"javascript.validate.enable": false
}
// Procfile
web: node dist
\ No newline at end of file
# DiabdocBackend
1. clone the project
2. run yarn install
3. run yarn build
4. run yarn start
This diff is collapsed.
{
"name": "dia-shoe-api",
"scripts": {
"build": "tsc",
"dev": "tsc --watch & NODE_ENV=development nodemon dist",
"start": "node dist"
},
"main": "lib/index.js",
"dependencies": {
"@types/node": "^12.6.9",
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"class-transformer": "^0.2.3",
"class-validator": "^0.9.1",
"cors": "^2.8.5",
"dotenv": "^8.0.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.6.4",
"mongoose-unique-validator": "^2.0.3",
"rand-token": "^0.4.0",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"tslint": "~5.18.0",
"typescript": "~3.5.3"
},
"private": true
}
import * as express from "express";
import Controller from "../shared/interfaces/controller.interface";
import actorsModel from "./actorsModel";
import { plainToClass, plainToClassFromExist } from "class-transformer";
import { validate } from "class-validator";
import { ActorsDto } from "./actorsDto";
const validationOptions = {
skipMissingProperties: true,
whitelist: false,
forbidNonWhitelisted: true,
};
export default class ActorController implements Controller {
public router = express.Router();
private actors = actorsModel;
private path = "/actors";
constructor() {
this.initializeRoutes();
}
//bind routes with functions
private initializeRoutes() {
this.router.post(`${this.path}/save`, this.createOrUpdateActors);
this.router.get(`${this.path}/multiple`, this.getAllActors);
}
// create new actors or update existing actors
private createOrUpdateActors = async (
request: express.Request,
response: express.Response
) => {
try {
const actorsDto: ActorsDto = plainToClass(ActorsDto, request.body, {
enableImplicitConversion: false,
});
const errors = await validate(actorsDto, validationOptions);
if (errors.length > 0) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad Request, Please refer documentation",
},
});
}
const updatedActors = await this.actors.findOneAndUpdate(
{
actorId: actorsDto.actorId,
},
{
$set: actorsDto,
},
{
new: true,
upsert: true,
}
);
const singleActor = plainToClassFromExist(
new ActorsDto(),
updatedActors.toJSON(),
{
excludeExtraneousValues: true,
}
);
if (!singleActor) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(201).json({
success: true,
data: singleActor,
});
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
//get list of all actors
private getAllActors = async (
request: express.Request,
response: express.Response
) => {
try {
const actorsDetails = await this.actors.find();
if (!actorsDetails) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(200).json({ success: true, data: actorsDetails });
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
}
import { IsString } from "class-validator";
import { Expose } from "class-transformer";
export class ActorsDto {
@Expose()
@IsString()
public actorId: String;
@Expose()
@IsString()
public actorName: String;
@Expose()
public freeDates: string[];
}
import * as mongoose from "mongoose";
const actorSchema = new mongoose.Schema(
{
actorId: {
type: String,
required: true,
},
actorName: {
type: String,
required: true,
},
freeDates: {
type: Array,
},
},
{ _id: false, timestamps: { createdAt: true } }
);
const actorModel = mongoose.model<mongoose.Document>("actors", actorSchema);
export default actorModel;
import "reflect-metadata";
import Init from "./init";
import ScriptController from "./script/scriptController";
import SceneController from "./scene/sceneController";
import InventoryController from "./inventory/inventoryController";
import ActorController from "./actors/actorsController";
import ScheduleController from "./timeTable/timeTableController";
const app = new Init([
new ScriptController(),
new SceneController(),
new InventoryController(),
new ActorController(),
new ScheduleController(),
]);
app.listen();
import * as bodyParser from "body-parser";
import * as express from "express";
import * as mongoose from "mongoose";
import * as cors from "cors";
import Controller from "./shared/interfaces/controller.interface";
import errorMiddleware from "./shared/middleware/error.middleware";
import { MONGO_URI } from "./shared/config/mongodb";
export default class Init {
public app: express.Application;
constructor(controllers: Controller[]) {
this.app = express();
this.connectToTheDatabase();
this.initializeMiddleware();
this.initializeControllers(controllers);
this.initializeErrorHandling();
}
private initializeMiddleware() {
this.app.use(bodyParser.json());
this.app.use(cors());
}
private initializeErrorHandling() {
this.app.use(errorMiddleware);
}
private initializeControllers(controllers: Controller[]) {
controllers.map((controller) => this.app.use("/", controller.router));
}
public listen() {
this.app.listen(process.env.PORT || 8080, () => {
console.log(`Connected to ${process.env.PORT || 8080}`);
});
}
private connectToTheDatabase() {
mongoose
.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
})
.then(() => console.log("MDB connected"))
.catch((error) => console.log(error));
}
}
import * as express from "express";
import Controller from "../shared/interfaces/controller.interface";
import inventoryModel from "./inventoryModel";
import { plainToClass, plainToClassFromExist } from "class-transformer";
import { validate } from "class-validator";
import { InventoryDto } from "./inventoryDto";
const validationOptions = {
skipMissingProperties: true,
whitelist: false,
forbidNonWhitelisted: true,
};
export default class InventoryController implements Controller {
public router = express.Router();
private inventory = inventoryModel;
private path = "/inventory";
constructor() {
this.initializeRoutes();
}
private initializeRoutes() {
this.router.post(`${this.path}/save`, this.createOrUpdateInventory);
this.router.get(`${this.path}/multiple/:scriptId`, this.getAllInventory);
}
private createOrUpdateInventory = async (
request: express.Request,
response: express.Response
) => {
try {
const inventoryDto: InventoryDto = plainToClass(
InventoryDto,
request.body,
{
enableImplicitConversion: false,
}
);
const errors = await validate(inventoryDto, validationOptions);
console.log(errors);
if (errors.length > 0) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad Request, Please refer documentation",
},
});
}
const updatedInventory = await this.inventory.findOneAndUpdate(
{
scriptId: inventoryDto.scriptId,
},
{
$set: inventoryDto,
},
{
new: true,
upsert: true,
}
);
const singleInventory = plainToClassFromExist(
new InventoryDto(),
updatedInventory.toJSON(),
{
excludeExtraneousValues: true,
}
);
if (!singleInventory) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(201).json({
success: true,
data: singleInventory,
});
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
private getAllInventory = async (
request: express.Request,
response: express.Response
) => {
try {
const inventoryDetails = await this.inventory.find({
scriptId: request.params.scriptId,
});
if (!inventoryDetails) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response
.status(200)
.json({ success: true, data: inventoryDetails });
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
}
import { IsString, ValidateNested, IsNumber, IsBoolean } from "class-validator";
import { Expose, Type } from "class-transformer";
export class InventoryTable {
@Expose()
@IsString()
public itemId: string;
@Expose()
@IsString()
public itemName: string;
@Expose()
@IsNumber()
public amount: number;
@Expose()
@IsBoolean()
public availability: boolean;
@Expose()
@IsNumber()
public cost: number;
}
export class InventoryDto {
@Expose()
@IsString()
public scriptId: String;
@Expose()
@Type(() => InventoryTable)
@ValidateNested()
public inventory: InventoryTable[];
}
import * as mongoose from "mongoose";
const inventorySchema = new mongoose.Schema(
{
scriptId: {
type: String,
required: true,
},
inventory: {
type: Array,
},
},
{ _id: false, timestamps: { createdAt: true } }
);
const inventoryModel = mongoose.model<mongoose.Document>(
"Inventory",
inventorySchema
);
export default inventoryModel;
import * as express from "express";
import Controller from "../shared/interfaces/controller.interface";
import sceneModel from "./sceneModel";
import { plainToClass, plainToClassFromExist } from "class-transformer";
import { validate } from "class-validator";
import { ScriptSceneDto } from "./scriptSceneDto";
const validationOptions = {
skipMissingProperties: true,
whitelist: false,
forbidNonWhitelisted: true,
};
export default class SceneController implements Controller {
public router = express.Router();
private scene = sceneModel;
private path = "/scene";
constructor() {
this.initializeRoutes();
}
//bind routes with functions
private initializeRoutes() {
this.router.post(`${this.path}/save`, this.createOrUpdateScene);
this.router.get(`${this.path}/multiple/:scriptId`, this.getAllScenes);
}
//creating or updating scenes
private createOrUpdateScene = async (
request: express.Request,
response: express.Response
) => {
try {
const sceneDto: ScriptSceneDto = plainToClass(
ScriptSceneDto,
request.body,
{
enableImplicitConversion: false,
}
);
const errors = await validate(sceneDto, validationOptions);
console.log(errors);
if (errors.length > 0) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad Request, Please refer documentation",
},
});
}
const updatedScene = await this.scene.findOneAndUpdate(
{
scriptId: sceneDto.scriptId,
id: sceneDto.id,
},
{
$set: sceneDto,
},
{
new: true,
upsert: true,
}
);
const singleScene = plainToClassFromExist(
new ScriptSceneDto(),
updatedScene.toJSON(),
{
excludeExtraneousValues: true,
}
);
if (!singleScene) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(201).json({
success: true,
data: singleScene,
});
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
// get the list of all scenes
private getAllScenes = async (
request: express.Request,
response: express.Response
) => {
try {
const scriptScenes = await this.scene.find({
scriptId: request.params.scriptId,
});
if (!scriptScenes) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(200).json({ success: true, data: scriptScenes });
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
}
import * as mongoose from "mongoose";
const sceneSchema = new mongoose.Schema(
{
id: {
type: String,
unique: true,
},
sceneNumber: {
type: String,
},
scriptId: {
type: String,
required: true,
},
location: {
type: String,
required: true,
},
dayPart: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
time: {
type: String,
required: true,
},
actors: {
type: Array,
},
inventory: {
type: Array,
},
crew: {
type: Array,
},
stories: {
type: Array,
},
weatherstatus: {
type: String,
},
},
{ _id: false, timestamps: { createdAt: true } }
);
const sceneModel = mongoose.model<mongoose.Document>("Scenes", sceneSchema);
export default sceneModel;
import {
IsString,
ValidateNested,
IsNumber,
IsDate,
IsBoolean,
} from "class-validator";
import { Expose, Type } from "class-transformer";
export class CrewDto {
@Expose()
@IsString()
public employeeId: string;
@Expose()
@IsString()
public employeeName: string;
@Expose()
@IsNumber()
public pricePerDay: number;
@Expose()
@IsBoolean()
public presentStatus: boolean;
}
export class StoryDto {
@Expose()
@IsString()
public storyId: string;
@Expose()
@IsString()
public shot: string;
@Expose()
@IsString()
public image: string;
}
export class ActorsDto {
@Expose()
@IsString()
public actorId: string;
@Expose()
@IsString()
public actorName: string;
}
export class InventoryDto {
@Expose()
@IsString()
public itemId: string;
@Expose()
@IsString()
public itemName: string;
@Expose()
@IsNumber()
public price: number;
}
export class ScriptSceneDto {
@Expose()
@IsString()
public id: String;
@Expose()
@IsString()
public sceneNumber: String;
@Expose()
@IsString()
public scriptId: String;
@Expose()
@IsString()
public location: String;
@Expose()
@IsString()
public dayPart: String;
@Expose()
@IsString()
public description: String;
@Expose()
@IsString()
public time: String;
@Expose()
@Type(() => ActorsDto)
@ValidateNested()
public actors: ActorsDto[];
@Expose()
@Type(() => InventoryDto)
@ValidateNested()
public inventory: InventoryDto[];
@Expose()
@Type(() => CrewDto)
@ValidateNested()
public crew: CrewDto[];
@Expose()
@Type(() => StoryDto)
@ValidateNested()
public stories: StoryDto[];
@Expose()
@IsString()
public weatherstatus: String;
}
import * as express from "express";
import Controller from "../shared/interfaces/controller.interface";
import scriptModel from "./scriptModel";
import { plainToClass, plainToClassFromExist } from "class-transformer";
import { validate, Validator } from "class-validator";
import { ScriptDto } from "./scriptDto";
const validationOptions = {
skipMissingProperties: true,
whitelist: true,
forbidNonWhitelisted: true,
};
export default class ScriptController implements Controller {
public router = express.Router();
private script = scriptModel;
private path = "/script";
constructor() {
console.log("initialized");
this.initializeRoutes();
}
//bind routes with functions
private initializeRoutes() {
this.router.post(`${this.path}/save`, this.saveScript);
this.router.get(`${this.path}/uniqueness`, this.checkIsUnique);
this.router.get(`${this.path}/multiple`, this.getAllScripts);
}
//save the script
private saveScript = async (
request: express.Request,
response: express.Response
) => {
try {
const scriptDto: ScriptDto = plainToClass(ScriptDto, request.body, {
enableImplicitConversion: false,
});
const errors = await validate(scriptDto, validationOptions);
if (errors.length > 0) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad Request, Please refer documentation",
},
});
}
const newScript = await this.script.findOneAndUpdate(
{
id: scriptDto.id,
},
{
$set: scriptDto,
},
{ new: true, safe: true, upsert: true }
);
if (!newScript) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(201).json({
success: true,
data: newScript,
});
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
//check uniqueness of script ID
private checkIsUnique = async (
request: express.Request,
response: express.Response
) => {
try {
const script = await this.script.findOne({
script: request.query.script,
});
if (!script) {
return response.status(200).json({ success: true });
}
return response.status(200).json({ success: false });
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
// get all the scripts
private getAllScripts = async (
request: express.Request,
response: express.Response
) => {
const scripts = await this.script.find();
if (!scripts) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(200).json({
success: true,
data: scripts,
});
};
}
import { IsString, IsArray } from "class-validator";
import { Expose } from "class-transformer";
export class ScriptDto {
@Expose()
@IsString()
public id: String;
@Expose()
@IsString()
public script: String;
}
export interface Script {
_id: string;
script: string;
}
import * as mongoose from "mongoose";
import { Script } from "./scriptInterface";
import * as uniqueValidator from "mongoose-unique-validator";
const scriptSchema = new mongoose.Schema({
id: {
type: String,
unique: true,
},
script: {
type: String,
unique: true,
},
});
scriptSchema.plugin(uniqueValidator, {
message: "{VALUE} is already taken.",
});
const scriptModel = mongoose.model<Script & mongoose.Document>(
"Scripts",
scriptSchema
);
export default scriptModel;
export const JWT_SECRET = "5c42c308591ea925e4eb5947";
export const MONGO_URI =
"mongodb+srv://admin:admin123@cluster0.ic3hj.mongodb.net/ScriptingCollection";
class HttpException extends Error {
public status: number;
public success: boolean;
public message: string;
constructor(status: number, message: string, success: boolean = true) {
super(message);
this.status = status;
this.success = success;
this.message = message;
}
}
export default HttpException;
import HttpException from "./HttpException";
export class InvalidRequestException extends HttpException {
constructor() {
super(400, `Invalid request.`, false);
}
}
export class AuthenticationTokenMissingException extends HttpException {
constructor() {
super(401, "Authentication token missing", false);
}
}
export class InvalidAuthenticationTokenException extends HttpException {
constructor() {
super(401, "Invalid authentication token", false);
}
}
export class UnauthorizedActionException extends HttpException {
constructor() {
super(403, "Unauthorized action.", false);
}
}
export class InternalServerError extends HttpException {
constructor() {
super(500, "Intenal server error.", false);
}
}
export class ForbiddenException extends HttpException {
constructor() {
super(403, "Forbidden.", false);
}
}
export class SearchKeywordMissingException extends HttpException {
constructor() {
super(400, `Search keyword is required.`, false);
}
}
import { Router } from "express";
interface Controller {
router: Router;
}
export default Controller;
import HttpException from "../exceptions/HttpException";
import { NextFunction, Request, Response } from "express";
function errorMiddleware(
error: HttpException,
request: Request,
response: Response,
next: NextFunction
) {
const status = error.status || 500;
const message = error.message || "Something went wrong";
response.status(status).send({
message,
status,
success: false,
});
}
export default errorMiddleware;
import * as express from "express";
import HttpException from "../exceptions/HttpException";
import { plainToClass } from "class-transformer";
import { validate, ValidationError } from "class-validator";
function validationMiddleware<T>(
type: any,
skipMissingProperties = false
): express.RequestHandler {
return ({ body }, response, next) => {
validate(plainToClass(type, body), { skipMissingProperties })
.then((errors: ValidationError[]) => {
if (errors.length > 0) {
const message = errors
.map((error: ValidationError) =>
Object["values"](error.constraints)
)
.join(", ");
next(new HttpException(400, message));
} else {
next();
}
})
.catch((e) => e);
};
}
export default validationMiddleware;
import * as express from "express";
import Controller from "../shared/interfaces/controller.interface";
import sceneModel from "../scene/sceneModel";
import { plainToClass, plainToClassFromExist } from "class-transformer";
import { validate } from "class-validator";
import { ScheduleDto, FinalScheduleDto } from "./timeTableDto";
import { ScriptSceneDto } from "../scene/scriptSceneDto";
import actorsModel from "../actors/actorsModel";
import { ActorsDto } from "../actors/actorsDto";
import ScheduleModel from "./timeTableModel";
const validationOptions = {
skipMissingProperties: true,
whitelist: false,
forbidNonWhitelisted: true,
};
export default class ScheduleController implements Controller {
public router = express.Router();
private scene = sceneModel;
private actor = actorsModel;
private schedule = ScheduleModel;
private path = "/schedule";
constructor() {
this.initializeRoutes();
}
//bind routes with functions
private initializeRoutes() {
this.router.get(`${this.path}/:scriptId`, this.getSchedule);
this.router.post(`${this.path}/save`, this.createOrUpdateSchedule);
}
//get all the free dates of actors
private getFreeDates = (dateList) => {
let result = [];
let lists = dateList;
for (let i = 0; i < lists.length; i++) {
let currentList = lists[i];
for (let y = 0; y < currentList.length; y++) {
let currentValue = currentList[y];
if (result.indexOf(currentValue) === -1) {
if (
lists.filter(function (obj) {
return obj.indexOf(currentValue) == -1;
}).length == 0
) {
result.push(currentValue);
}
}
}
}
return result;
};
//schedule dates for single scene
private getSchedule = async (
request: express.Request,
response: express.Response
) => {
try {
const scriptScenes: ScriptSceneDto[] = await this.scene.find({
scriptId: request.params.scriptId,
});
if (!scriptScenes) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
const actorsInScenes: ActorsDto[] = await this.actor.find();
let schedule = scriptScenes.map((scene) => {
let newSchedule = new ScheduleDto();
newSchedule.dayPart = scene.dayPart;
newSchedule.inventory = scene.inventory.map(({ itemName }) => itemName);
newSchedule.location = scene.location;
newSchedule.sceneNumber = scene.sceneNumber;
newSchedule.scriptId = scene.scriptId;
newSchedule.time = scene.time;
let selectedActors: ActorsDto[] = scene.actors.map(({ actorId }) => {
let actor = actorsInScenes.filter(
(actor) => actor.actorId === actorId
);
return actor[0];
});
let toBeSelectedDates = [];
selectedActors.map((actor) => {
toBeSelectedDates.push(actor.freeDates);
});
newSchedule.dates = this.getFreeDates(toBeSelectedDates);
return newSchedule;
});
return response.status(200).json({ success: true, data: schedule });
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
//create or update schedules
private createOrUpdateSchedule = async (
request: express.Request,
response: express.Response
) => {
try {
const scheduleDto: FinalScheduleDto = plainToClass(
FinalScheduleDto,
request.body,
{
enableImplicitConversion: false,
}
);
const errors = await validate(scheduleDto, validationOptions);
if (errors.length > 0) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad Request, Please refer documentation",
error: errors,
},
});
}
const updatedSchedule = await this.schedule.findOneAndUpdate(
{
scriptId: scheduleDto.scriptId,
},
{
$set: scheduleDto,
},
{
new: true,
upsert: true,
}
);
const singleSchedule = plainToClassFromExist(
new FinalScheduleDto(),
updatedSchedule.toJSON(),
{
excludeExtraneousValues: true,
}
);
if (!singleSchedule) {
return response.status(400).json({
success: false,
data: {
errorCode: 400,
errorMessage: "Bad request, please refer documentation",
},
});
}
return response.status(201).json({
success: true,
data: singleSchedule,
});
} catch (error) {
return response.status(500).json({
success: false,
data: {
errorCode: 500,
error,
errorMessage: "Internal Server Error",
},
});
}
};
}
import {
IsString,
ValidateNested,
IsNumber,
IsDate,
IsBoolean,
} from "class-validator";
import { Expose, Type } from "class-transformer";
export class ScheduleDto {
@Expose()
@IsString()
public sceneNumber: String;
@Expose()
@IsString()
public scriptId: String;
@Expose()
@IsString()
public location: String;
@Expose()
@IsString()
public dayPart: String;
@Expose()
@IsString()
public time: String;
@Expose()
@ValidateNested()
public inventory: String[];
@Expose()
@ValidateNested()
public dates: String[];
}
class SingleScheduleDto {
@Expose()
@IsString()
public sceneNumber: String;
@Expose()
@IsString()
public scriptId: String;
@Expose()
@IsString()
public location: String;
@Expose()
@IsString()
public dayPart: String;
@Expose()
@IsString()
public time: String;
@Expose()
@ValidateNested()
public inventory: String[];
@Expose()
@ValidateNested()
public dates: String[];
@Expose()
@ValidateNested()
public fixedDate: String[];
}
export class FinalScheduleDto {
@Expose()
@IsString()
public scriptId: String;
@Expose()
@Type(() => SingleScheduleDto)
public schedule: SingleScheduleDto[];
}
import * as mongoose from "mongoose";
const scheduleSchema = new mongoose.Schema(
{
scriptId: {
type: String,
required: true,
},
schedule: {
type: Array,
},
},
{ _id: false, timestamps: { createdAt: true } }
);
const scheduleModel = mongoose.model<mongoose.Document>(
"Schedule",
scheduleSchema
);
export default scheduleModel;
{
"compilerOptions": {
"lib": ["es7", "dom"],
"module": "commonjs",
"noImplicitReturns": true,
"outDir": "dist",
"sourceMap": true,
"target": "es6",
"experimentalDecorators": true,
"allowJs": true
},
"compileOnSave": true,
"include": ["src"]
}
{
"rules": {
// -- Strict errors --
// These lint rules are likely always a good idea.
// Force function overloads to be declared together. This ensures readers understand APIs.
"adjacent-overload-signatures": true,
// Do not allow the subtle/obscure comma operator.
"ban-comma-operator": true,
// Do not allow internal modules or namespaces . These are deprecated in favor of ES6 modules.
"no-namespace": true,
// Do not allow parameters to be reassigned. To avoid bugs, developers should instead assign new values to new vars.
"no-parameter-reassignment": true,
// Force the use of ES6-style imports instead of /// <reference path=> imports.
"no-reference": true,
// Do not allow type assertions that do nothing. This is a big warning that the developer may not understand the
// code currently being edited (they may be incorrectly handling a different type case that does not exist).
"no-unnecessary-type-assertion": true,
// Disallow nonsensical label usage.
"label-position": true,
// Disallows the (often typo) syntax if (var1 = var2). Replace with if (var2) { var1 = var2 }.
"no-conditional-assignment": true,
// Disallows constructors for primitive types (e.g. new Number('123'), though Number('123') is still allowed).
"no-construct": true,
// Do not allow super() to be called twice in a constructor.
"no-duplicate-super": true,
// Do not allow the same case to appear more than once in a switch block.
"no-duplicate-switch-case": true,
// Do not allow a variable to be declared more than once in the same block. Consider function parameters in this
// rule.
"no-duplicate-variable": [true, "check-parameters"],
// Disallows a variable definition in an inner scope from shadowing a variable in an outer scope. Developers should
// instead use a separate variable name.
"no-shadowed-variable": true,
// Empty blocks are almost never needed. Allow the one general exception: empty catch blocks.
"no-empty": [true, "allow-empty-catch"],
// Functions must either be handled directly (e.g. with a catch() handler) or returned to another function.
// This is a major source of errors in Cloud Functions and the team strongly recommends leaving this rule on.
"no-floating-promises": true,
// Do not allow any imports for modules that are not in package.json. These will almost certainly fail when
// deployed.
"no-implicit-dependencies": true,
// The 'this' keyword can only be used inside of classes.
"no-invalid-this": true,
// Do not allow strings to be thrown because they will not include stack traces. Throw Errors instead.
"no-string-throw": true,
// Disallow control flow statements, such as return, continue, break, and throw in finally blocks.
"no-unsafe-finally": true,
// Do not allow variables to be used before they are declared.
"no-use-before-declare": true,
// Expressions must always return a value. Avoids common errors like const myValue = functionReturningVoid();
"no-void-expression": [true, "ignore-arrow-function-shorthand"],
// Disallow duplicate imports in the same file.
"no-duplicate-imports": true,
// -- Strong Warnings --
// These rules should almost never be needed, but may be included due to legacy code.
// They are left as a warning to avoid frustration with blocked deploys when the developer
// understand the warning and wants to deploy anyway.
// Warn when an empty interface is defined. These are generally not useful.
"no-empty-interface": {"severity": "warning"},
// Warn when an import will have side effects.
"no-import-side-effect": {"severity": "warning"},
// Warn when variables are defined with var. Var has subtle meaning that can lead to bugs. Strongly prefer const for
// most values and let for values that will change.
"no-var-keyword": {"severity": "warning"},
// Prefer === and !== over == and !=. The latter operators support overloads that are often accidental.
"triple-equals": {"severity": "warning"},
// Warn when using deprecated APIs.
"deprecation": {"severity": "warning"},
// -- Light Warnigns --
// These rules are intended to help developers use better style. Simpler code has fewer bugs. These would be "info"
// if TSLint supported such a level.
// prefer for( ... of ... ) to an index loop when the index is only used to fetch an object from an array.
// (Even better: check out utils like .map if transforming an array!)
"prefer-for-of": {"severity": "warning"},
// Warns if function overloads could be unified into a single function with optional or rest parameters.
"unified-signatures": {"severity": "warning"},
// Warns if code has an import or variable that is unused.
"no-unused-variable": {"severity": "warning"},
// Prefer const for values that will not change. This better documents code.
"prefer-const": {"severity": "warning"},
// Multi-line object liiterals and function calls should have a trailing comma. This helps avoid merge conflicts.
"trailing-comma": {"severity": "warning"}
},
"defaultSeverity": "error"
}
NODE_PATH=src/
\ No newline at end of file
/src/serviceWorker.js
\ No newline at end of file
{
"parser": "babel-eslint",
"plugins": ["react", "promise", "flowtype"],
"globals": {
"module": true,
"process": true
},
"parserOptions": {
"ecmaVersion": 6,
"ecmaFeatures": {
"classes": true,
"jsx": true
}
},
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:flowtype/recommended"
],
"rules": {
"strict": 1,
"no-console": 1,
"react/no-danger": 1,
"react/prop-types": 1,
"no-unused-vars": 1,
"semi": 1,
"semi-style": 1,
"semi-spacing": 1
}
}
[ignore]
[include]
[libs]
[lints]
[options]
module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=src
module.file_ext=.scss
module.name_mapper.extension='scss' -> 'empty/object'
[strict]
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
{
"javascript.updateImportsOnFileMove.enabled": "always",
"javascript.preferences.importModuleSpecifier": "non-relative",
"cSpell.words": ["devtools"],
"editor.formatOnSave": true,
"editor.formatOnPaste": true
}
## Available Scripts
In the project directory, you can run:
### `yarn start`
Runs the app in the development mode.<br>
### `yarn test`
Launches the test runner in the interactive watch mode.<br>
### `yarn flow`
Launches the flow test runner.<br>
### `yarn lint`
Launches the lint test runner.<br>
### `test:coverage`
Launches the test coverage of code.<br>
### `test:all`
Run all tests.<br>
### `yarn run build`
Builds the app for production to the `build` folder.<br>
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"jsx": "preserve",
"checkJs": true,
"baseUrl": "./src"
},
"exclude": ["node_modules", "**/node_modules/*"]
}
{
"name": "gaia-dashboard",
"version": "0.1.0",
"private": true,
"dependencies": {
"add": "^2.0.6",
"classnames": "^2.2.6",
"connected-react-router": "^6.4.0",
"history": "^4.9.0",
"node-sass": "^4.12.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-redux": "^7.0.3",
"react-router": "^5.0.0",
"react-router-dom": "^5.0.0",
"react-scripts": "3.0.1",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"yarn": "^1.16.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"flow": "flow",
"flow:setup": "flow-typed install",
"test:coverage": "jest --coverage",
"test:all": "yarn lint && yarn flow",
"lint": "eslint src --ext .js,.jsx,.json"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"eslint": "^5.16.0",
"eslint-loader": "^2.1.2",
"eslint-plugin-flowtype": "^3.9.1",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-react": "^7.13.0",
"flow-bin": "^0.98.1",
"flow-typed": "^2.5.2",
"husky": "^2.3.0",
"prettier-eslint": "^8.8.2",
"redux-devtools-extension": "^2.13.8"
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
rel="stylesheet"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
// @flow
import {
ASYNC_ACTORS_INIT,
INITIALIZE_ACTORS,
HANDLE_NOTIFICATION,
GET_ALL_ACTORS_SUCCESS,
ON_ADD_FREE_DATE,
} from "actionTypes/actors";
import Alert from "components/Alert";
export function initializeActors() {
return (dispatch) => {
dispatch({ type: INITIALIZE_ACTORS });
};
}
function asyncActorsInit() {
return {
type: ASYNC_ACTORS_INIT,
};
}
//handle notifications for actors
export function notificationHandler(isSuccess, message) {
return {
type: HANDLE_NOTIFICATION,
payload: {
isSuccess,
notification: {
type: isSuccess ? Alert.TYPE.SUCCESS : Alert.TYPE.ERROR,
message,
},
},
};
}
//get all actors from API
export function getAllActors() {
return (dispatch, getState, serviceManager) => {
dispatch(asyncActorsInit());
let actorsService = serviceManager.get("ActorsService");
actorsService
.getAllActors()
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_ACTORS_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
//update details of actor
export function updateActors(payload: object) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncActorsInit());
let actorsService = serviceManager.get("ActorsService");
actorsService
.saveActors(payload)
.then(({ success }) => {
if (success) {
actorsService
.getAllActors()
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_ACTORS_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(
false,
"Something went wrong. Please try again"
)
);
});
}
dispatch(
notificationHandler(
success,
success ? "Actors saved successfully." : "Failed to save actors."
)
);
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
//add free dates for actor
export function addFreeDate(actorId, freeDate) {
return (dispatch) => {
dispatch({ type: ON_ADD_FREE_DATE, payload: { actorId, freeDate } });
};
}
// @flow
import {
ASYNC_AUTH_INIT,
ON_SAVE_SCRIPT_DATA,
ON_SAVE_INVENTORY_DATA,
SET_INITIAL_SCRIPTS,
INITIALIZE_AUTH_INIT,
} from "actionTypes/auth";
function asyncInitializeAuthInit() {
return {
type: INITIALIZE_AUTH_INIT,
};
}
export function asyncAuthInit() {
return {
type: ASYNC_AUTH_INIT,
};
}
export function addNewScript(payload) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncInitializeAuthInit());
};
}
export function saveScriptData(payload) {
return (dispatch) => {
dispatch({ type: ON_SAVE_SCRIPT_DATA, payload });
};
}
export function saveInventoryData(payload) {
return (dispatch) => {
dispatch({ type: ON_SAVE_INVENTORY_DATA, payload });
};
}
export function setInitialScripts() {
return (dispatch) => {
let scripts = JSON.parse(localStorage.getItem("scripts"));
if (scripts) {
dispatch({ type: SET_INITIAL_SCRIPTS, payload: scripts });
}
};
}
// @flow
import {
ASYNC_INVENTORY_INIT,
INITIALIZE_INVENTORY,
HANDLE_NOTIFICATION,
GET_ALL_INVENTORY_SUCCESS,
ON_CHANGE_INVENTORY_FIELD,
ON_ADD_NEW_INVENTORY,
ON_REMOVE_INVENTORY,
} from "actionTypes/inventory";
import Alert from "components/Alert";
//initilizing the inventory
export function initializeInventory() {
return (dispatch) => {
dispatch({ type: INITIALIZE_INVENTORY });
};
}
function asyncInventoryInit() {
return {
type: ASYNC_INVENTORY_INIT,
};
}
//handle notification for inventory
export function notificationHandler(isSuccess, message) {
return {
type: HANDLE_NOTIFICATION,
payload: {
isSuccess,
notification: {
type: isSuccess ? Alert.TYPE.SUCCESS : Alert.TYPE.ERROR,
message,
},
},
};
}
//get all inventory from API
export function getAllInventory(scriptId: string) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncInventoryInit());
let inventoryService = serviceManager.get("InventoryService");
inventoryService
.getAllInventory(scriptId)
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_INVENTORY_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
//update inventory details
export function updateInventory(payload: object) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncInventoryInit());
let inventoryService = serviceManager.get("InventoryService");
inventoryService
.saveInventory(payload)
.then(({ success }) => {
if (success) {
inventoryService
.getAllInventory(payload.scriptId)
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_INVENTORY_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(
false,
"Something went wrong. Please try again"
)
);
});
}
dispatch(
notificationHandler(
success,
success
? "Inventory saved successfully."
: "Failed to save inventory."
)
);
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
export function onChangeInventoryField(payload: string) {
return (dispatch) => {
dispatch({ type: ON_CHANGE_INVENTORY_FIELD, payload });
};
}
export function onAddNewInventory(payload: Object) {
return (dispatch) => {
dispatch({ type: ON_ADD_NEW_INVENTORY, payload });
};
}
export function onRemoveInventory(payload: string) {
return (dispatch) => {
dispatch({ type: ON_REMOVE_INVENTORY, payload });
};
}
// @flow
import {
ASYNC_SCENE_INIT,
INITIALIZE_SCENE,
HANDLE_NOTIFICATION,
GET_ALL_SCENES_SUCCESS,
ON_CHANGE_SCENE_FIELD,
ON_ADD_NEW_LAYER,
ON_SELECT_SINGLE_SCENE,
ON_ADD_NEW_STORY,
ON_CHANGE_STORY_FIELD,
} from "actionTypes/scene";
import Alert from "components/Alert";
//intialize scene reduces
export function initializeScene() {
return (dispatch) => {
dispatch({ type: INITIALIZE_SCENE });
};
}
function asyncSceneInit() {
return {
type: ASYNC_SCENE_INIT,
};
}
//handle notifications for scenes
export function notificationHandler(isSuccess, message) {
return {
type: HANDLE_NOTIFICATION,
payload: {
isSuccess,
notification: {
type: isSuccess ? Alert.TYPE.SUCCESS : Alert.TYPE.ERROR,
message,
},
},
};
}
//get all scenes from API
export function getAllScenes(scriptId: string) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncSceneInit());
let sceneService = serviceManager.get("SceneService");
sceneService
.getAllScenes(scriptId)
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_SCENES_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
//update scene details
export function updateScene(payload: object) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncSceneInit());
let sceneService = serviceManager.get("SceneService");
sceneService
.saveScene(payload)
.then(({ success }) => {
if (success) {
sceneService
.getAllScenes(payload.scriptId)
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_SCENES_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(
false,
"Something went wrong. Please try again"
)
);
});
}
dispatch(
notificationHandler(
success,
success ? "Scene saved successfully." : "Failed to save scene."
)
);
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
export function onChangeSceneField(payload: Object) {
return (dispatch) => {
dispatch({ type: ON_CHANGE_SCENE_FIELD, payload });
};
}
export function onAddNewLayer(payload: Object) {
return (dispatch) => {
dispatch({ type: ON_ADD_NEW_LAYER, payload });
};
}
export function onSelectSingleScene(sceneId: string) {
return (dispatch) => {
dispatch({ type: ON_SELECT_SINGLE_SCENE, payload: sceneId });
};
}
export function onAddNewStory(payload: Object) {
return (dispatch) => {
dispatch({ type: ON_ADD_NEW_STORY, payload });
};
}
export function onChangeStoryField(payload: Object) {
return (dispatch) => {
dispatch({ type: ON_CHANGE_STORY_FIELD, payload });
};
}
// @flow
import {
ASYNC_SCHEDULE_INIT,
HANDLE_NOTIFICATION,
INITIALIZE_SCHEDULE,
GET_SCHEDULE_SUCCESS,
ON_CHANGE_SCENE_NUMBER,
} from "actionTypes/schedule";
import Alert from "components/Alert";
//initialize schedule
export function initializeSchedule() {
return (dispatch) => {
dispatch({ type: INITIALIZE_SCHEDULE });
};
}
function asyncScheduleInit() {
return {
type: ASYNC_SCHEDULE_INIT,
};
}
//handle schedule notifications
export function notificationHandler(isSuccess, message) {
return {
type: HANDLE_NOTIFICATION,
payload: {
isSuccess,
notification: {
type: isSuccess ? Alert.TYPE.SUCCESS : Alert.TYPE.ERROR,
message,
},
},
};
}
//get list of schedule
export function getAllSchedule(scriptId: string) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncScheduleInit());
let scheduleService = serviceManager.get("ScheduleService");
scheduleService
.getSchedule(scriptId)
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_SCHEDULE_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
export function onChangeScene(payload) {
return (dispatch) => {
dispatch({ type: ON_CHANGE_SCENE_NUMBER, payload });
};
}
export function saveSchedule(payload: Object) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncScheduleInit());
let scheduleService = serviceManager.get("ScheduleService");
scheduleService
.saveSchedule(payload)
.then(({ success }) => {
dispatch(
notificationHandler(
success,
success
? "Schedule saved successfully"
: "Something went wrong. Please try again"
)
);
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
// @flow
import {
ASYNC_SCRIPT_INIT,
INITIALIZE_SCRIPT,
HANDLE_NOTIFICATION,
GET_ALL_SCRIPTS_SUCCESS,
} from "actionTypes/script";
import Alert from "components/Alert";
//initialize the script
export function initializeScript() {
return (dispatch) => {
dispatch({ type: INITIALIZE_SCRIPT });
};
}
function asyncScriptInit() {
return {
type: ASYNC_SCRIPT_INIT,
};
}
//handle notifications for scripts
export function notificationHandler(isSuccess, message) {
return {
type: HANDLE_NOTIFICATION,
payload: {
isSuccess,
notification: {
type: isSuccess ? Alert.TYPE.SUCCESS : Alert.TYPE.ERROR,
message,
},
},
};
}
//add new script
export function addNewScript(payload: Object) {
return (dispatch, getState, serviceManager) => {
dispatch(asyncScriptInit());
let scriptService = serviceManager.get("ScriptService");
scriptService
.isUniqueScript({ script: payload.script })
.then(({ success }) => {
if (success) {
scriptService
.saveScript(payload)
.then(({ success }) => {
dispatch(
notificationHandler(
success,
success
? "Script added successfully."
: "Failed to add script. Please try again"
)
);
})
.catch(() => {
dispatch(
notificationHandler(
false,
"Something went wrong. Please try again"
)
);
});
} else {
dispatch(
notificationHandler(success, "Script name is already in use.")
);
}
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
//get all scripts from API
export function getAllScripts() {
return (dispatch, getState, serviceManager) => {
dispatch(asyncScriptInit());
let scriptService = serviceManager.get("ScriptService");
scriptService
.getAllScripts()
.then(({ success, data }) => {
if (success) {
dispatch({ type: GET_ALL_SCRIPTS_SUCCESS, payload: data });
} else {
dispatch(
notificationHandler(
success,
"Something went wrong. Please try again"
)
);
}
})
.catch(() => {
dispatch(
notificationHandler(false, "Something went wrong. Please try again")
);
});
};
}
export const PREFIX = "@@actors/";
export const ASYNC_ACTORS_INIT = `${PREFIX}ASYNC_ACTORS_INIT`;
export const INITIALIZE_ACTORS = `${PREFIX}INITIALIZE_ACTORS`;
export const HANDLE_NOTIFICATION = `${PREFIX}HANDLE_NOTIFICATION`;
export const GET_ALL_ACTORS_SUCCESS = `${PREFIX}GET_ALL_ACTORS_SUCCESS`;
export const ON_ADD_FREE_DATE = `${PREFIX}ON_ADD_FREE_DATE`;
export const PREFIX = "@@auth/";
export const ASYNC_AUTH_INIT = `${PREFIX}ASYNC_AUTH_INIT`;
export const INITIALIZE_AUTH_INIT = `${PREFIX}INITIALIZE_AUTH_INIT`;
export const HANDLE_NOTIFICATION = `${PREFIX}HANDLE_NOTIFICATION`;
export const ADD_NEW_SCRIPT_SUCCESS = `${PREFIX}ADD_NEW_SCRIPT_SUCCESS`;
export const ON_SAVE_SCRIPT_DATA = `${PREFIX}ON_SAVE_SCRIPT_DATA`;
export const ON_SAVE_INVENTORY_DATA = `${PREFIX}ON_SAVE_INVENTORY_DATA`;
export const SET_INITIAL_SCRIPTS = `${PREFIX}SET_INITIAL_SCRIPTS`;
export const PREFIX = "@@inventory/";
export const ASYNC_INVENTORY_INIT = `${PREFIX}ASYNC_INVENTORY_INIT`;
export const INITIALIZE_INVENTORY = `${PREFIX}INITIALIZE_INVENTORY`;
export const HANDLE_NOTIFICATION = `${PREFIX}HANDLE_NOTIFICATION`;
export const GET_ALL_INVENTORY_SUCCESS = `${PREFIX}GET_ALL_INVENTORY_SUCCESS`;
export const ON_CHANGE_INVENTORY_FIELD = `${PREFIX}ON_CHANGE_INVENTORY_FIELD`;
export const ON_ADD_NEW_INVENTORY = `${PREFIX}ON_ADD_NEW_INVENTORY`;
export const ON_REMOVE_INVENTORY = `${PREFIX}ON_REMOVE_INVENTORY`;
export const PREFIX = "@@scene/";
export const ASYNC_SCENE_INIT = `${PREFIX}ASYNC_SCENE_INIT`;
export const INITIALIZE_SCENE = `${PREFIX}INITIALIZE_SCENE`;
export const HANDLE_NOTIFICATION = `${PREFIX}HANDLE_NOTIFICATION`;
export const GET_ALL_SCENES_SUCCESS = `${PREFIX}GET_ALL_SCENES_SUCCESS`;
export const ON_CHANGE_SCENE_FIELD = `${PREFIX}ON_CHANGE_SCENE_FIELD`;
export const ON_ADD_NEW_LAYER = `${PREFIX}ON_ADD_NEW_LAYER`;
export const ON_SELECT_SINGLE_SCENE = `${PREFIX}ON_SELECT_SINGLE_SCENE`;
export const ON_ADD_NEW_STORY = `${PREFIX}ON_ADD_NEW_STORY`;
export const ON_CHANGE_STORY_FIELD = `${PREFIX}ON_CHANGE_STORY_FIELD`;
export const PREFIX = "@@schedule/";
export const ASYNC_SCHEDULE_INIT = `${PREFIX}ASYNC_SCHEDULE_INIT`;
export const INITIALIZE_SCHEDULE = `${PREFIX}INITIALIZE_SCHEDULE`;
export const HANDLE_NOTIFICATION = `${PREFIX}HANDLE_NOTIFICATION`;
export const GET_SCHEDULE_SUCCESS = `${PREFIX}GET_SCHEDULE_SUCCESS`;
export const ON_CHANGE_SCENE_NUMBER = `${PREFIX}ON_CHANGE_SCENE_NUMBER`;
export const PREFIX = "@@script/";
export const ASYNC_SCRIPT_INIT = `${PREFIX}ASYNC_SCRIPT_INIT`;
export const INITIALIZE_SCRIPT = `${PREFIX}INITIALIZE_SCRIPT`;
export const HANDLE_NOTIFICATION = `${PREFIX}HANDLE_NOTIFICATION`;
export const GET_ALL_SCRIPTS_SUCCESS = `${PREFIX}GET_ALL_SCRIPTS_SUCCESS`;
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g transform="translate(1 1)">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-49.8646" y1="651.7271" x2="-48.9365" y2="650.692" gradientTransform="matrix(494.9333 0 0 -443.7334 24704.9688 289234)">
<stop offset="0" style="stop-color:#ffddad"/>
<stop offset="0.172" style="stop-color:#ffddad"/>
<stop offset="0.2" style="stop-color:#ffddad"/>
<stop offset="0.2" style="stop-color:#DAE2F2"/>
<stop offset="0.201" style="stop-color:#EBEAF2"/>
<stop offset="0.201" style="stop-color:#F6EFF2"/>
<stop offset="0.202" style="stop-color:#FDF2F2"/>
<stop offset="0.205" style="stop-color:#FFF3F2"/>
<stop offset="0.252" style="stop-color:#FFF3F2"/>
<stop offset="0.26" style="stop-color:#FFF3F2"/>
<stop offset="0.26" style="stop-color:#ffddad"/>
<stop offset="0.397" style="stop-color:#ffddad"/>
<stop offset="0.42" style="stop-color:#ffddad"/>
<stop offset="0.42" style="stop-color:#DAE2F2"/>
<stop offset="0.421" style="stop-color:#EBEAF2"/>
<stop offset="0.421" style="stop-color:#F6EFF2"/>
<stop offset="0.422" style="stop-color:#FDF2F2"/>
<stop offset="0.425" style="stop-color:#FFF3F2"/>
<stop offset="0.489" style="stop-color:#FFF3F2"/>
<stop offset="0.5" style="stop-color:#FFF3F2"/>
<stop offset="0.5" style="stop-color:#F8F0F2"/>
<stop offset="0.5" style="stop-color:#E7E8F2"/>
<stop offset="0.5" style="stop-color:#DCE3F2"/>
<stop offset="0.5" style="stop-color:#D5E0F2"/>
<stop offset="0.501" style="stop-color:#ffddad"/>
<stop offset="0.706" style="stop-color:#ffddad"/>
<stop offset="0.74" style="stop-color:#ffddad"/>
<stop offset="0.741" style="stop-color:#FFF3F2"/>
<stop offset="0.835" style="stop-color:#FFF3F2"/>
<stop offset="0.85" style="stop-color:#FFF3F2"/>
<stop offset="0.851" style="stop-color:#ffddad"/>
</linearGradient>
<path style="fill:url(#SVGID_1_);" d="M502.467,58.734v409.6c0,18.851-15.282,34.133-34.133,34.133H41.667
c-18.851,0-34.133-15.282-34.133-34.133v-409.6H502.467z"/>
<path style="fill:#fcc488;" d="M502.467,41.667V75.8H7.534V41.667c0-18.851,15.282-34.133,34.133-34.133h426.667
C487.185,7.534,502.467,22.816,502.467,41.667z"/>
<g>
<path style="fill:#FFF2F2;" d="M476.867,41.667V75.8H7.534V41.667c0-18.851,15.282-34.133,34.133-34.133h401.067
C461.585,7.534,476.867,22.816,476.867,41.667z"/>
<polygon style="fill:#FFF2F2;" points="135.534,186.734 50.2,306.2 135.534,306.2 135.534,272.067 "/>
<polygon style="fill:#FFF2F2;" points="417.134,186.734 331.8,306.2 417.134,306.2 417.134,272.067 "/>
</g>
<path style="fill:#fcc488;" d="M229.4,178.2h42.667c14.138,0,25.6,11.462,25.6,25.6v136.533c0,14.138-11.462,25.6-25.6,25.6H229.4
c-14.138,0-25.6-11.462-25.6-25.6V203.8C203.8,189.662,215.262,178.2,229.4,178.2z"/>
<path style="fill:#FFF2F2;" d="M229.4,178.2h17.067c14.139,0,25.6,11.462,25.6,25.6v136.533c0,14.138-11.461,25.6-25.6,25.6H229.4
c-14.138,0-25.6-11.462-25.6-25.6V203.8C203.8,189.662,215.262,178.2,229.4,178.2z"/>
<path style="fill:#FFFFFF;" d="M33.134,468.334V41.667c0-18.851,15.282-34.133,34.133-34.133h-25.6
c-18.851,0-34.133,15.282-34.133,34.133v426.667c0,18.851,15.282,34.133,34.133,34.133h25.6
C48.416,502.467,33.134,487.185,33.134,468.334z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="0,469.334 0,358.4 0,42.667 17.067,42.667 17.067,358.4 17.067,469.334 "/>
<g>
<path style="fill:#F1A841;" d="M5.291,408.918c0.484,0.247,1.001,0.419,1.536,0.512c0.564,0.095,1.135,0.152,1.707,0.171
c2.259-0.033,4.422-0.917,6.059-2.475c0.771-0.816,1.38-1.772,1.792-2.816c1.287-3.183,0.586-6.825-1.792-9.301
c-0.816-0.771-1.772-1.38-2.816-1.792c-1.026-0.437-2.128-0.669-3.243-0.683c-2.259,0.033-4.422,0.917-6.059,2.475
c-3.3,3.365-3.3,8.752,0,12.117l1.28,1.024C4.231,408.473,4.747,408.731,5.291,408.918z"/>
<path style="fill:#F1A841;" d="M469.334,0H42.667C19.115,0.028,0.029,19.114,0,42.667v324.267c0,4.713,3.821,8.533,8.533,8.533
s8.533-3.82,8.533-8.533v-281.6h477.867v384c0,14.138-11.462,25.6-25.6,25.6H42.667c-14.138,0-25.6-11.462-25.6-25.6V435.2
c0-4.713-3.821-8.533-8.533-8.533S0,430.487,0,435.2v34.133C0.029,492.886,19.115,511.972,42.667,512h426.667
c23.552-0.028,42.638-19.114,42.667-42.667V42.667C511.972,19.114,492.886,0.028,469.334,0z M17.067,68.267v-25.6
c0-14.138,11.462-25.6,25.6-25.6h426.667c14.138,0,25.6,11.462,25.6,25.6v25.6H17.067z"/>
<path style="fill:#F1A841;" d="M59.734,34.134H51.2c-4.713,0-8.533,3.821-8.533,8.533S46.488,51.2,51.2,51.2h8.533
c4.713,0,8.533-3.821,8.533-8.533S64.447,34.134,59.734,34.134z"/>
<path style="fill:#F1A841;" d="M99.926,36.608c-3.422-3.157-8.695-3.157-12.117,0c-0.753,0.83-1.359,1.782-1.792,2.816
c-0.911,2.066-0.911,4.419,0,6.485c0.435,1.033,1.041,1.985,1.792,2.816c2.442,2.443,6.126,3.153,9.301,1.792
c1.033-0.435,1.985-1.041,2.816-1.792c0.751-0.831,1.357-1.783,1.792-2.816c0.91-2.066,0.91-4.419,0-6.485
C101.285,38.391,100.679,37.438,99.926,36.608z"/>
<path style="fill:#F1A841;" d="M134.059,36.608c-3.422-3.157-8.695-3.157-12.117,0c-0.753,0.83-1.359,1.782-1.792,2.816
c-1.361,3.175-0.651,6.859,1.792,9.301c0.83,0.753,1.782,1.359,2.816,1.792c2.066,0.911,4.419,0.911,6.485,0
c1.043-0.414,1.999-1.022,2.816-1.792c1.602-1.607,2.493-3.789,2.475-6.059c0.025-1.119-0.209-2.229-0.683-3.243
C135.418,38.391,134.812,37.438,134.059,36.608z"/>
<path style="fill:#F1A841;" d="M460.801,34.134H170.667c-4.713,0-8.533,3.821-8.533,8.533s3.82,8.533,8.533,8.533h290.133
c4.713,0,8.533-3.821,8.533-8.533S465.513,34.134,460.801,34.134z"/>
<path style="fill:#F1A841;" d="M136.534,281.6c4.713,0,8.533-3.82,8.533-8.533V179.2c0-0.282-0.137-0.512-0.162-0.794
c-0.051-0.58-0.165-1.152-0.341-1.707c-0.154-0.508-0.357-0.999-0.606-1.468c-0.242-0.464-0.528-0.904-0.853-1.314
c-0.366-0.456-0.781-0.871-1.237-1.237c-0.213-0.179-0.333-0.418-0.563-0.572c-0.23-0.154-0.478-0.162-0.708-0.29
c-0.512-0.276-1.053-0.497-1.613-0.657c-0.537-0.165-1.088-0.276-1.647-0.333c-0.264-0.068-0.532-0.122-0.802-0.162
c-0.282,0-0.529,0.137-0.802,0.162c-0.579,0.058-1.15,0.172-1.707,0.341c-0.503,0.155-0.992,0.355-1.459,0.597
c-0.475,0.257-0.924,0.56-1.34,0.905c-0.444,0.367-0.853,0.776-1.22,1.22c-0.171,0.213-0.418,0.341-0.572,0.572l-85.333,128
c-1.747,2.618-1.911,5.986-0.426,8.762c1.485,2.776,4.377,4.508,7.525,4.508h119.467c4.713,0,8.533-3.82,8.533-8.533
s-3.821-8.533-8.533-8.533H67.141L128,207.36v65.707C128,277.78,131.821,281.6,136.534,281.6z"/>
<path style="fill:#F1A841;" d="M128,341.334v25.6c0,4.713,3.821,8.533,8.533,8.533s8.533-3.82,8.533-8.533v-25.6
c0-4.713-3.82-8.533-8.533-8.533S128,336.621,128,341.334z"/>
<path style="fill:#F1A841;" d="M418.134,281.6c4.713,0,8.533-3.82,8.533-8.533V179.2c0-0.282-0.137-0.512-0.162-0.794
c-0.051-0.58-0.165-1.152-0.341-1.707c-0.154-0.508-0.357-0.999-0.606-1.468c-0.242-0.464-0.528-0.904-0.853-1.314
c-0.366-0.456-0.781-0.871-1.237-1.237c-0.213-0.179-0.333-0.418-0.563-0.572c-0.23-0.154-0.478-0.162-0.708-0.29
c-0.512-0.276-1.053-0.497-1.613-0.657c-0.537-0.165-1.088-0.276-1.647-0.333c-0.264-0.068-0.532-0.122-0.802-0.162
c-0.282,0-0.529,0.137-0.802,0.162c-0.579,0.056-1.151,0.171-1.707,0.341c-0.508,0.152-1,0.355-1.468,0.606
c-0.474,0.251-0.92,0.551-1.331,0.896c-0.451,0.363-0.863,0.772-1.229,1.22c-0.171,0.213-0.418,0.341-0.572,0.572l-85.333,128
c-1.748,2.62-1.911,5.989-0.424,8.765c1.487,2.776,4.382,4.507,7.532,4.504h119.467c4.713,0,8.533-3.82,8.533-8.533
s-3.821-8.533-8.533-8.533H348.741l60.86-91.307v65.707C409.6,277.78,413.421,281.6,418.134,281.6z"/>
<path style="fill:#F1A841;" d="M409.6,341.334v25.6c0,4.713,3.82,8.533,8.533,8.533s8.533-3.82,8.533-8.533v-25.6
c0-4.713-3.821-8.533-8.533-8.533S409.6,336.621,409.6,341.334z"/>
<path style="fill:#F1A841;" d="M273.067,375.467c18.851,0,34.133-15.282,34.133-34.133V204.8
c0-18.851-15.282-34.133-34.133-34.133H230.4c-18.851,0-34.133,15.282-34.133,34.133v136.533
c0,18.851,15.282,34.133,34.133,34.133H273.067z M213.334,341.334V204.8c0-9.426,7.641-17.067,17.067-17.067h42.667
c9.426,0,17.067,7.641,17.067,17.067v136.533c0,9.426-7.641,17.067-17.067,17.067H230.4
C220.975,358.4,213.334,350.759,213.334,341.334z"/>
<path style="fill:#F1A841;" d="M162.134,426.667c0,4.713,3.82,8.533,8.533,8.533h170.667c4.713,0,8.533-3.82,8.533-8.533
s-3.82-8.533-8.533-8.533H170.667C165.954,418.134,162.134,421.954,162.134,426.667z"/>
<path style="fill:#F1A841;" d="M204.8,452.267c-4.713,0-8.533,3.82-8.533,8.533s3.821,8.533,8.533,8.533h102.4
c4.713,0,8.533-3.82,8.533-8.533s-3.821-8.533-8.533-8.533H204.8z"/>
</g>
</g>
</g>
</svg>
// @flow
import React from "react";
import classNames from "classnames";
import "./styles.scss";
const TYPE = {
SUCCESS: "alert-success",
ERROR: "alert-danger",
LIGHT: "alert-light",
INFO: "alert-info"
};
type AlertProps = {
isFullWidth: boolean,
children: any,
type: typeof TYPE.SUCCESS | typeof TYPE.ERROR | typeof TYPE.INFO
};
function Alert(props: AlertProps) {
const { children, type, isFullWidth } = props;
return (
<div className={classNames("alert", { "full-width": isFullWidth }, type)}>
{children}
</div>
);
}
Alert.TYPE = TYPE;
Alert.defaultProps = {
type: TYPE.INFO,
isFullWidth: false
};
export default Alert;
import Alert from "./Alert";
export default Alert;
.alert {
position: relative;
padding: 0.75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: 0.25rem;
&.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
&.alert-success {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
a {
color: #0e4c1c;
font-weight: 500;
}
}
&.alert-light {
color: #818182;
background-color: #fbfbfb;
border-color: #e5e5e6;
}
&.alert-info {
color: #383d41;
background-color: #e2e3e5;
border-color: #d6d8db;
}
&.full-width {
width: 100%;
text-align: center;
}
}
// @flow
import React, { Component, Fragment } from "react";
import { Link } from "react-router-dom";
import Icon from "components/icon";
import Button from "components/button";
import "./styles.scss";
const HTML_TYPE = {
BUTTON: "button",
SUBMIT: "submit",
LINK: "Link"
};
type ButtonDropdownProps = {
options: Array<any>,
name: string
};
type ButtonDropdownState = {
isOpen: boolean
};
class ButtonDropdown extends Component<
ButtonDropdownProps,
ButtonDropdownState
> {
static HTML_TYPE = HTML_TYPE;
constructor(props: SelectProps) {
super(props);
this.state = {
isOpen: false
};
// $FlowFixMe
this.toggleDropdown = this.toggleDropdown.bind(this);
// $FlowFixMe
this.closeDropdown = this.closeDropdown.bind(this);
}
toggleDropdown() {
const { options } = this.props;
if (options && options.length > 0) {
this.setState(({ isOpen }) => ({
isOpen: !isOpen
}));
}
}
closeDropdown() {
this.setState({
isOpen: false
});
}
getOptions = () => {
const { options } = this.props;
if (options && options.length > 0) {
return (
<Fragment>
{options.map(({ buttonName, type, onClickButton }, key) => {
if (type === ButtonDropdown.HTML_TYPE.LINK) {
return (
<li key={key}>
<Link to={onClickButton}>{buttonName}</Link>
</li>
);
}
return (
<li key={key} onClick={onClickButton}>
{buttonName}
</li>
);
})}
</Fragment>
);
}
};
render() {
const { isOpen } = this.state;
const { options, name } = this.props;
return (
<div className="button-container">
<Button onClick={this.toggleDropdown}>
{name}
<span className="dropdown-icon">
{isOpen && options && options.length > 0 ? (
<Icon icon="chevron-up" />
) : (
<Icon icon="chevron-down" />
)}
</span>
</Button>
{isOpen && (
<div className="button-dropdown">
<ul className="dropdown">{this.getOptions()} </ul>
</div>
)}
{isOpen && <div className="backdrop" onClick={this.closeDropdown} />}
</div>
);
}
}
export default ButtonDropdown;
import ButtonDropdown from "./ButtonDropdown";
export default ButtonDropdown;
.button-container {
position: relative;
.button-dropdown {
margin-top: 5px;
position: absolute;
z-index: 999;
background: #f8f8f8;
min-width: 150px;
left: 0;
.dropdown {
list-style: none;
padding: 0;
margin: 0;
border: 1px solid #f8f8f8;
width: 100%;
text-align: left;
li {
border-top: 1px solid #e1e7e9;
padding: 10px;
color: #888da2;
font-size: 12px;
cursor: pointer;
a {
text-decoration: none;
color: #888da2;
}
}
}
}
.dropdown-icon {
margin-left: 10px;
}
.backdrop {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
}
// @flow
import React from "react";
import classNames from "classnames";
import "./styles.scss";
type ColProps = {
children: any,
size?: string,
xm?: string,
sm?: string,
md?: string,
lg?: string,
styles?: string,
className?: string
};
export default function Col(props: ColProps) {
const { xm, sm, md, lg, size, className, styles } = props;
const colClass = [];
if (size) {
colClass.push(`col-${size}`);
}
if (xm) {
colClass.push(`col-xm-${xm}`);
}
if (sm) {
colClass.push(`col-sm-${sm}`);
}
if (md) {
colClass.push(`col-md-${md}`);
}
if (lg) {
colClass.push(`col-lg-${lg}`);
}
return (
<div className={classNames("col", className, colClass)} style={styles}>
{props.children}
</div>
);
}
import Col from "./Col";
export default Col;
.col {
position: relative;
width: 100%;
min-height: 1px;
padding-right: 10px;
padding-left: 10px;
flex-basis: 0;
flex-grow: 1;
}
.col-1 {
flex: 0 0 8.33%;
max-width: 8.33%;
}
.col-2 {
flex: 0 0 16.66%;
max-width: 16.66%;
}
.col-3 {
flex: 0 0 24.99%;
max-width: 24.99%;
}
.col-4 {
flex: 0 0 33.32%;
max-width: 33.32%;
}
.col-5 {
flex: 0 0 41.65%;
max-width: 41.65%;
}
.col-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-7 {
flex: 0 0 58.33%;
max-width: 58.33%;
}
.col-8 {
flex: 0 0 66.66%;
max-width: 66.66%;
}
.col-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-10 {
flex: 0 0 83.33%;
max-width: 83.33%;
}
.col-11 {
flex: 0 0 50%;
max-width: 50%;
}
.col-12 {
flex: 0 0 100%;
max-width: 100%;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
// breakpoints map
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
// media query mixin
@mixin break($size) {
@media (min-width: map-get($breakpoints, $size)) {
@content;
}
}
// number of columns variable
$items: 12;
// loop over the breakpoints
@each $key, $value in $breakpoints {
@for $i from 1 through $items {
.col-#{$key}-#{$i} {
flex: 0 0 100%;
max-width: 100%;
@include break($key) {
flex: 0 0 #{$i / $items * 100%};
max-width: #{$i / $items * 100%};
}
}
}
}
import React, { Component } from "react";
import "./styles.scss";
class DotLoader extends Component {
render() {
return (
<div className="dot-loader">
<div />
<div />
<div />
<div />
</div>
);
}
}
export default DotLoader;
.dot-loader {
display: inline-block;
position: relative;
width: 64px;
height: 32px;
div {
position: absolute;
top: 23px;
width: 7px;
height: 7px;
border-radius: 50%;
background: #000;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
&:nth-child(1) {
left: 6px;
animation: dot-loader-1 0.6s infinite;
}
&:nth-child(2) {
left: 6px;
animation: dot-loader-2 0.6s infinite;
}
&:nth-child(3) {
left: 26px;
animation: dot-loader-2 0.6s infinite;
}
&:nth-child(4) {
left: 45px;
animation: dot-loader-3 0.6s infinite;
}
}
}
@keyframes dot-loader-1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes dot-loader-3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes dot-loader-2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(19px, 0);
}
}
// @flow
import React, { Component, Fragment } from "react";
import Icon from "components/icon";
import Row from "components/Row";
import Col from "components/Col";
import "./styles.scss";
type ExpandableListProps = {
onClick: Function,
isOpen: Boolean,
title: {
name: string,
subTitles?: {
category: string,
isDefault: Boolean,
label: string,
path: string,
method: string,
allow: Boolean
}[]
}
};
type ExpandableListState = {
isToggleOpen: Boolean,
};
class ExpandableList extends Component<
ExpandableListProps,
ExpandableListState
> {
static defaultProps = {
onClick: () => {}
};
constructor(props) {
super(props);
this.state = {
isToggleOpen: true
};
// $FlowFixMe
this.toggleDropdown = this.toggleDropdown.bind(this);
// $FlowFixMe
this.handleInnerLock = this.handleInnerLock.bind(this);
}
toggleDropdown() {
this.setState({
...this.state,
isToggleOpen: !this.state.isToggleOpen
});
}
handleInnerLock(label) {
const {
title,
title: { subTitles }
} = this.props;
let updatedSubTitles = subTitles.map(subTitle => {
if (label === subTitle.label) {
return {
...subTitle,
allow: !subTitle.allow
};
}
return subTitle;
});
this.props.onClick({
...title,
subTitles: updatedSubTitles
});
}
getSubTitle = () => {
const {
title: { subTitles }
} = this.props;
if (subTitles && subTitles.length > 0) {
return (
<Fragment>
{subTitles.map(subTitle => {
return (
<Row key={`${subTitle.label}`}>
{!subTitle.isDefault && (
<Col className={"childLess-title"} size="2">
<li key={`${subTitle.label}`}>{subTitle.label}</li>
</Col>
)}
{!subTitle.isDefault && (
<Col>
<div
className="collapse-icon"
onClick={() => {
this.handleInnerLock(subTitle.label);
}}
>
{subTitle.allow ? (
<Icon icon="padlock-unlocked" />
) : (
<Icon icon="padlock-locked" />
)}
</div>
</Col>
)}
</Row>
);
})}
</Fragment>
);
} else return null;
};
render() {
const {
isOpen,
title: { name, subTitles }
} = this.props;
const { isToggleOpen } = this.state;
return (
<div className="expand-list-container">
<div className="title-container">
<Row>
{subTitles && subTitles.length > 0 && (
<div className="collapse-icon" onClick={this.toggleDropdown}>
{isToggleOpen && isOpen ? (
<Icon icon="chevron-up" />
) : (
<Icon icon="chevron-down" />
)}
</div>
)}
<Col
className={
subTitles && subTitles.length > 0
? "childWith-title"
: "childLess-title"
}
size="2"
>
{name}
</Col>
</Row>
</div>
{isToggleOpen && isOpen && (
<div className="sub-title-container">
<ul className="dropdown">{this.getSubTitle()}</ul>
</div>
)}
</div>
);
}
}
export default ExpandableList;
import ExpandableList from "./ExpandableList";
export default ExpandableList;
.expand-list-container {
padding-top: 10px;
.title-container {
font-size: 12px;
font-weight: bold;
.collapse-icon {
padding-left: 30px;
border: 0px #e6e6e6;
min-height: 10px;
}
}
.sub-title-container {
font-size: 12px;
.dropdown {
list-style: none;
li {
padding: 5px;
//padding-left: 15px;
}
}
}
.childLess-title {
padding-left: 20px;
}
.childWith-title {
padding-top: 2px;
padding-bottom: 2px;
}
}
// @flow
import React, { Component } from "react";
type FileInputProps = {
onChange: Function,
multiple: boolean,
placeholder: string,
className: string,
accept: string
};
class FileInput extends Component<FileInputProps> {
fileInput = React.createRef();
render() {
const { onChange, multiple, placeholder, className, accept } = this.props;
return (
<div className={className}>
<input
type="file"
placeholder={placeholder}
className="file-input"
multiple={multiple}
onChange={onChange}
accept={accept}
/>
</div>
);
}
}
export default FileInput;
import FileInput from "./FileInput";
export default FileInput;
.file-input {
position: absolute;
width: 100%;
height: 100%;
outline: none;
opacity: 0;
margin: 0;
padding: 0;
cursor: pointer;
}
// @flow
import React, { PureComponent } from "react";
import Icon from "components/icon";
export type IconButtonProps = {
icon: string,
className?: string,
onClick?: Function,
disabled: boolean
};
export default class IconButton extends PureComponent<IconButtonProps> {
static defaultProps = {
disabled: false
};
handleClick(e: SyntheticEvent<HTMLElement>) {
if (this.props.onClick !== undefined) {
this.props.onClick(e);
}
}
render() {
const { className, icon, disabled } = this.props;
return (
<button
onClick={this.handleClick.bind(this)}
className={className}
disabled={disabled}
>
<Icon icon={icon} fixedWidth />
</button>
);
}
}
// @flow
import React, { PureComponent } from "react";
import Icon from "components/icon";
export type IconButtonProps = {
icon: string,
className?: string,
onClick?: Function,
disabled: boolean
};
export default class IconButton extends PureComponent<IconButtonProps> {
static defaultProps = {
disabled: false
};
handleClick(e: SyntheticEvent<HTMLElement>) {
if (this.props.onClick !== undefined) {
this.props.onClick(e);
}
}
render() {
const { className, icon, disabled } = this.props;
return (
<button
onClick={this.handleClick.bind(this)}
className={className}
disabled={disabled}
>
<Icon icon={icon} fixedWidth />
</button>
);
}
}
// @flow
import React from "react";
import classNames from "classnames";
import "./styles.scss";
type InputProps = {
text: string,
placeholder: string,
name?: string,
id?: string,
onChange: Function,
type: string,
error: null | string,
className: string,
onBlur: Function,
onFocus: Function,
autoComplete: boolean,
disabled: boolean,
onKeyPress?: Function
};
export default function Input(props: InputProps) {
const {
text,
placeholder,
name,
id,
onChange,
type,
error,
className,
onBlur,
onFocus,
autoComplete,
disabled,
onKeyPress
} = props;
const hasErrors = error !== null;
return (
<div
className={classNames(
"form-input",
{ "has-errors": hasErrors },
className
)}
>
<input
autoComplete={autoComplete.toString()}
name={name}
id={id}
placeholder={placeholder}
value={text === null ? "" : text}
onChange={event => onChange(event.target.value)}
type={type}
onBlur={onBlur}
onFocus={onFocus}
disabled={disabled}
onKeyPress={onKeyPress}
/>
{getFieldErrors(error)}
</div>
);
}
function getFieldErrors(error: string | null) {
return error !== null && <div className="form-errors">{error}</div>;
}
Input.defaultProps = {
type: "text",
error: null,
className: "",
onChange: () => {},
onBlur: () => {},
onFocus: () => {},
autoComplete: true,
disabled: false
};
import Input from "./Input";
export default Input;
.form-input {
position: relative;
margin-bottom: 15px;
i {
.input-icon {
position: absolute;
top: 10px;
left: 10px;
}
}
input {
padding: 10px 20px;
width: 100%;
box-shadow: none;
border: none;
}
&.has-errors {
input {
border-color: #cc6363 !important;
box-shadow: 0 0 2px 1px #ffa3a3;
}
}
.form-errors {
list-style: none;
text-align: left;
font-size: 12px;
padding: 10px 0 0;
color: #cc6363;
}
}
// @flow
import React from "react";
import { Link } from "react-router-dom";
export default function link({
to,
children,
...rest
}: {
to: string,
children: any
}) {
return (
<Link to={`/${to}`} {...rest}>
{children}
</Link>
);
}
// @flow
import React, { PureComponent } from "react";
import classNames from "classnames";
import IconButton from "components/IconButton";
import "./styles.scss";
type ModalProps = {
isTransparent: boolean,
className: string,
showModal: boolean,
children: any,
onClose: Function
};
type ModalState = {
showModal: boolean
};
class Modal extends PureComponent<ModalProps, ModalState> {
static defaultProps = {
isFullScreen: false,
showModal: false
};
state = {
showModal: false
};
toggleModal = (showModal: boolean) => {
this.setState(function() {
return {
showModal
};
});
};
componentDidUpdate(prevProps: ModalProps) {
if (prevProps.showModal !== this.props.showModal)
this.toggleModal(this.props.showModal);
}
render() {
const {
isTransparent,
children,
className,
showModal,
onClose
} = this.props;
return (
<div
className={classNames(
"modal",
className,
{
"modal-transparent": isTransparent
},
{ "show-modal": showModal }
)}
>
<IconButton className="modal-close" onClick={onClose} icon="close" />
<div className="modal-content">
<div className="mobile-modal-close">
<IconButton onClick={onClose} icon="" />
</div>
{children}
</div>
</div>
);
}
}
export default Modal;
import Modal from "./Modal";
export default Modal;
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #686868;
display: flex;
align-items: center;
align-content: center;
box-shadow: 0 0 30px 5px #00000026;
display: none;
z-index: 100;
&.show-modal {
display: flex;
}
&.modal-transparent {
background: transparent;
}
.modal-content {
max-width: 1200px;
width: 90%;
max-height: 657px;
overflow: auto;
margin: 0 auto;
background: #fff;
padding: 20px;
position: relative;
.mobile-modal-close {
display: none;
@media (max-width: 768px) {
display: block;
text-align: right;
button {
background: none;
font-size: 22px;
color: #8c8888;
border: 2px solid;
margin: 15px;
border-radius: 50%;
}
}
}
}
.modal-close {
position: fixed;
top: 30px;
right: 30px;
background: none;
border: none;
font-size: 35px;
color: #ffffff;
}
}
// @flow
import React, { PureComponent, Fragment } from "react";
import classnames from "classnames";
import Icon from "components/icon";
import Checkbox from "components/checkbox";
import "./styles.scss";
type MultiSelectObject = {
value: string,
name: string,
isSelected: Boolean,
};
type MultiSelectInputType = MultiSelectObject | string;
type MultiSelectProps = {
onChange: Function,
options: MultiSelectInputType[],
placeholder?: string,
};
type MultiSelectState = {
isOpen: boolean,
options: MultiSelectObject[],
};
class MultiSelect extends PureComponent<MultiSelectProps, MultiSelectState> {
static defaultProps = {
onChange: () => {},
};
constructor(props: MultiSelectProps) {
super(props);
this.state = {
isOpen: false,
options: [],
};
//$FlowFixMe Forced to do that for perf issues
this.toggleDropdown = this.toggleDropdown.bind(this);
// $FlowFixMe
this.closeDropdown = this.closeDropdown.bind(this);
// $FlowFixMe
this.onClick = this.onClick.bind(this);
// $FlowFixMe
this.filterSelectedOptions = this.filterSelectedOptions.bind(this);
}
componentDidMount() {
const options = this.getFormattedOptions(this.props);
this.setState({
options,
});
}
componentDidUpdate(prevProps: MultiSelectProps) {
if (prevProps.options !== this.props.options) {
const options = this.getFormattedOptions(this.props);
this.setState({
options,
});
}
}
getFormattedOptions(props: MultiSelectProps) {
const { options } = props;
// $FlowFixMe
return options.map((option) => {
let name, value, isSelected;
if (typeof option === "object") {
name = option.name;
value = option.value;
isSelected = option.isSelected;
} else {
name = option;
value = option;
isSelected = false;
}
return {
name,
value,
isSelected,
};
});
}
toggleDropdown() {
const { options } = this.props;
const { isOpen } = this.state;
if (isOpen) {
this.filterSelectedOptions();
}
if (options && options.length > 0) {
this.setState(({ isOpen }) => ({
isOpen: !isOpen,
}));
}
}
closeDropdown() {
this.setState({
isOpen: false,
});
}
onClick(
name: $PropertyType<MultiSelectObject, "name">,
value: $PropertyType<MultiSelectObject, "value">,
isSelected: $PropertyType<MultiSelectObject, "isSelected">
) {
let updatedOptions = this.state.options.map((item) => {
if (item.name === name) {
return { ...item, isSelected: !isSelected };
}
return item;
});
this.setState(
{
...this.state,
options: updatedOptions,
}
//this.filterSelectedOptions
);
}
filterSelectedOptions() {
let selectedOptions = this.state.options.filter((option) => {
return option.isSelected;
});
this.props.onChange(selectedOptions);
}
onSearchChange(e: SyntheticEvent<HTMLButtonElement>) {
const options = this.getFormattedOptions(this.props);
const userInput = e.currentTarget.value;
// Filter our options that don't contain the user's input
let filteredOptions = options.filter(
(option) =>
option.name.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
if (filteredOptions.length === 0) {
filteredOptions = [
{
name: "No Results",
value: "no-results",
},
];
}
this.setState({
options: filteredOptions,
});
}
getOptions = () => {
const { options } = this.state;
if (options && options.length > 0) {
return (
<Fragment>
{options.map(({ value, name, isSelected }, key) => (
<li key={`${key}_${value}`}>
<Checkbox
onChange={() => {
this.onClick(name, value, isSelected);
}}
isChecked={isSelected}
>
{name}
</Checkbox>
</li>
))}
</Fragment>
);
}
};
getFieldErrors(error: string | null) {
return error !== null ? (
<ul className="form-errors">
<li>{error}</li>
</ul>
) : (
""
);
}
render() {
const { isOpen } = this.state;
const { options, placeholder } = this.props;
//const isSelected = selectedName !== null || selectedValue !== null;
return (
<div className="multi-select-container">
<div className="form-group">
<div
className={classnames("form-select", {
"dropdown-open": isOpen,
})}
>
<div className="selected-dropdown" onClick={this.toggleDropdown}>
<span className="placeholder">{placeholder}</span>
<span className="selector pull-right">
{isOpen && options && options.length > 0 ? (
<Icon icon="chevron-up" />
) : (
<Icon icon="chevron-down" />
)}
</span>
</div>
<div className="dropdown-container">
<ul className="dropdown">{this.getOptions()} </ul>
</div>
</div>
{isOpen && <div className="backdrop" onClick={this.toggleDropdown} />}
</div>
</div>
);
}
}
export default MultiSelect;
import MultiSelect from "./MultiSelect";
export default MultiSelect;
.multi-select-container {
margin-bottom: 15px;
.form-group {
.form-select {
position: relative;
text-transform: uppercase;
font-weight: 600;
font-size: 12px;
text-align: center;
min-width: 100px;
cursor: pointer;
.selected-dropdown {
padding: 10px;
border: 1px solid #e6e6e6;
border-radius: 5px;
min-height: 37px;
.pull-right {
float: right;
}
}
i {
color: #000000;
}
.placeholder {
font-size: 13px;
color: #000000;
}
.dropdown-container {
position: absolute;
z-index: 999;
background: #f8f8f8;
width: 100%;
left: 0;
.dropdown {
list-style: none;
max-height: 200px;
overflow-y: scroll;
padding: 0;
margin: 0;
border: 1px solid #f8f8f8;
display: none;
width: 100%;
text-align: left;
li {
padding: 10px;
&.search-input {
padding: 0;
position: relative;
i {
position: absolute;
left: 10px;
top: calc(50% - 7px);
font-size: 14px;
color: #ababab;
}
input {
padding: 10px 10px 10px 30px;
width: 100%;
}
}
}
}
}
&.dropdown-open {
.selected-dropdown {
border-bottom-color: transparent;
}
.dropdown {
border-top-color: transparent;
display: block;
}
}
}
.form-errors {
list-style: none;
text-align: left;
font-size: 12px;
padding: 10px 0 0;
li {
padding-bottom: 10px;
color: #cc6363;
&:last-child {
padding-bottom: 0;
}
}
}
.backdrop {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
}
}
import Pagination from "./Pagination";
export default Pagination;
.pagination-block {
width: 100%;
display: flex;
justify-content: center;
margin: 50px 0;
.navigation-button {
background: none;
border: none;
background-color: #1583ff;
color: #ffffff;
padding: 0 20px;
border-radius: 3px;
font-weight: 600;
letter-spacing: 1px;
&:disabled {
background-color: #60a9e5;
}
}
.pagination {
list-style: none;
margin: 0;
text-align: center;
padding: 0 10px;
.page-item {
display: inline-block;
margin: 0 5px;
&.separator {
width: 40px;
text-align: center;
margin: 0 5px;
}
button {
line-height: 40px;
width: 40px;
text-align: center;
font-size: 12px;
border-radius: 2px;
color: #1e1e1e;
border: none;
background: none;
}
&.active {
button {
background-color: #1583ff;
color: #ffffff;
}
}
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment