V3-760 Create logic for New project popup (#714)

* V3-760 Create logic for New project popup

* removing path to static files from gateway

* added missing copyrights
This commit is contained in:
Yehor Butko 2018-11-27 13:14:10 +00:00 committed by GitHub
parent 3a87de391f
commit 784f3a887d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 200 additions and 22 deletions

View File

@ -26,6 +26,7 @@ type gateway struct {
func (gw *gateway) run() { func (gw *gateway) run() {
mux := http.NewServeMux() mux := http.NewServeMux()
//gw.config.StaticPath = "./web/satellite"
fs := http.FileServer(http.Dir(gw.config.StaticPath)) fs := http.FileServer(http.Dir(gw.config.StaticPath))
mux.Handle("/api/graphql/v0", http.HandlerFunc(gw.grapqlHandler)) mux.Handle("/api/graphql/v0", http.HandlerFunc(gw.grapqlHandler))

View File

@ -159,7 +159,7 @@ func (s *Service) GetUsersProjects(ctx context.Context) ([]Project, error) {
return s.store.Projects().GetAll(ctx) return s.store.Projects().GetAll(ctx)
} }
// CreateProject is a method for querying all projects // CreateProject is a method for creating new project
func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (*Project, error) { func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (*Project, error) {
// TODO: auth will be moved in future // TODO: auth will be moved in future
user, err := s.Authorize(ctx) user, err := s.Authorize(ctx)

View File

@ -13,7 +13,8 @@
"@types/graphql": "^14.0.3", "@types/graphql": "^14.0.3",
"apollo-cache-inmemory": "^1.3.9", "apollo-cache-inmemory": "^1.3.9",
"apollo-client": "^2.4.5", "apollo-client": "^2.4.5",
"apollo-link": "^1.2.3", "apollo-link": "^1.2.4",
"apollo-link-context": "^1.0.10",
"apollo-link-http": "^1.5.5", "apollo-link-http": "^1.5.5",
"graphql": "^14.0.2", "graphql": "^14.0.2",
"graphql-tag": "^2.10.0", "graphql-tag": "^2.10.0",

View File

@ -6,14 +6,24 @@
<div v-if="!isOptional" class="label-container"> <div v-if="!isOptional" class="label-container">
<img v-if="error" src="../../../static/images/register/ErrorInfo.svg"/> <img v-if="error" src="../../../static/images/register/ErrorInfo.svg"/>
<h3 v-if="!error">{{label}}</h3> <h3 v-if="!error">{{label}}</h3>
<h3 class="label-container__add-label">{{additionalLabel}}</h3> <h3 v-if="!error" class="label-container__add-label">{{additionalLabel}}</h3>
<h3 class="label-container__error" v-if="error">{{error}}</h3> <h3 class="label-container__error" v-if="error">{{error}}</h3>
</div> </div>
<div v-if="isOptional" class="optional-label-container"> <div v-if="isOptional" class="optional-label-container">
<h3>{{label}}</h3> <h3>{{label}}</h3>
<h4>Optional</h4> <h4>Optional</h4>
</div> </div>
<textarea v-if="isMultiline" :id="this.$props.label" :placeholder="this.$props.placeholder" v-model="value" :style="style" :rows="5" :cols="40" wrap="hard"/> <textarea
v-if="isMultiline"
:id="this.$props.label"
:placeholder="this.$props.placeholder"
v-model="value"
:style="style"
:rows="5"
:cols="40"
wrap="hard"
@input="onInput" >
</textarea>
<input <input
v-if="!isMultiline" v-if="!isMultiline"
@input="onInput" @input="onInput"

View File

@ -14,13 +14,14 @@
additionalLabel="Up To 20 Characters" additionalLabel="Up To 20 Characters"
placeholder="Enter Project Name" placeholder="Enter Project Name"
width="30vw" width="30vw"
:error="nameError"
@setData="setProjectName"> @setData="setProjectName">
</HeaderedInput> </HeaderedInput>
<HeaderedInput <HeaderedInput
label="Company Name" label="Company Name"
placeholder="Enter Company Name" placeholder="Enter Company Name"
width="30vw" width="30vw"
@setData="setCompanyName"> @setData="">
</HeaderedInput> </HeaderedInput>
<HeaderedInput <HeaderedInput
label="Description" label="Description"
@ -28,15 +29,17 @@
isMultiline isMultiline
height="10vh" height="10vh"
width="30vw" width="30vw"
@setData="setDescription"> @setData="setProjectDescription">
</HeaderedInput> </HeaderedInput>
<div class="new-project-popup__form-container__terms-area"> <div class="new-project-popup__form-container__terms-area">
<Checkbox class="new-project-popup__form-container__terms-area__checkbox" @setData="setTermsAccepted"/> <Checkbox class="new-project-popup__form-container__terms-area__checkbox"
@setData="setTermsAccepted"
:isCheckboxError="termsAcceptedError"/>
<h2>I agree to the Storj Bridge Hosting <a>Terms & Conditions</a></h2> <h2>I agree to the Storj Bridge Hosting <a>Terms & Conditions</a></h2>
</div> </div>
<div class="new-project-popup__form-container__button-container"> <div class="new-project-popup__form-container__button-container">
<Button label="Cancel" width="14vw" height="48px" :onPress="onCloseClick" isWhite/> <Button label="Cancel" width="14vw" height="48px" :onPress="onCloseClick" isWhite/>
<Button label="Create Project" width="14vw" height="48px" :onPress="onCreate"/> <Button label="Create Project" width="14vw" height="48px" :onPress="createProject"/>
</div> </div>
</div> </div>
<div class="new-project-popup__close-cross-container"> <div class="new-project-popup__close-cross-container">
@ -64,9 +67,53 @@ import Button from "@/components/common/Button.vue";
type: Function type: Function
} }
}, },
data: function() {
return {
name: "",
description: "",
isTermsAccepted: false,
termsAcceptedError: false,
nameError: "",
}
},
methods: { methods: {
onCloseClick: function () : void { setProjectName: function(value: string) : void {
this.$data.name = value;
this.$data.nameError = "";
},
setProjectDescription: function(value: string) : void {
this.$data.description = value;
},
setTermsAccepted: function(value: boolean) : void {
this.$data.isTermsAccepted = value;
this.$data.termsAcceptedError = false;
},
onCloseClick: function() : void {
// TODO: save popup states in store
this.$emit("onClose"); this.$emit("onClose");
},
createProject: function() : void {
if (!this.$data.isTermsAccepted) {
this.$data.termsAcceptedError = true;
}
if (!this.$data.name) {
this.$data.nameError = "Name is required!";
}
if (this.$data.name.length > 20) {
this.$data.nameError = "Name should be less than 21 character!";
}
if (this.$data.nameError || this.$data.isTermsAcceptedError) {
return;
}
this.$store.dispatch("createProject", {
name: this.$data.name,
description: this.$data.description,
isTermsAccepted: this.$data.isTermsAccepted,
})
} }
}, },
components: { components: {

View File

@ -4,7 +4,7 @@
<template> <template>
<div class="project-selection-container"> <div class="project-selection-container">
<div class="project-selection-toggle-container" v-on:click="toggleSelection"> <div class="project-selection-toggle-container" v-on:click="toggleSelection">
<h1>{{projectName}}</h1> <h1>{{name}}</h1>
<div class="project-selection-toggle-container__expander-area"> <div class="project-selection-toggle-container__expander-area">
<img v-if="!isChoiceShown" src="../../../../static/images/register/BlueExpand.svg" /> <img v-if="!isChoiceShown" src="../../../../static/images/register/BlueExpand.svg" />
<img v-if="isChoiceShown" src="../../../../static/images/register/BlueHide.svg" /> <img v-if="isChoiceShown" src="../../../../static/images/register/BlueHide.svg" />
@ -24,7 +24,7 @@ import ProjectSelectionDropdown from "./ProjectSelectionDropdown.vue"
return { return {
isChoiceShown: false, isChoiceShown: false,
// this.$store.selectedProject // this.$store.selectedProject
projectName: "Choose Project" name: "Choose Project"
} }
}, },
methods: { methods: {

View File

@ -5,12 +5,15 @@ import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import {authModule} from "@/store/modules/auth"; import {authModule} from "@/store/modules/auth";
import {projectsModule} from "@/store/modules/projects";
Vue.use(Vuex); Vue.use(Vuex);
// Satellite store (vuex)
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
authModule authModule,
projectsModule
} }
}); });

View File

@ -2,8 +2,7 @@
// See LICENSE for copying information. // See LICENSE for copying information.
import { import {
SET_USER_INFO, AUTH_MUTATIONS,
REVERT_TO_DEFAULT_USER_INFO,
} from "../mutationConstants"; } from "../mutationConstants";
export const authModule = { export const authModule = {
@ -21,7 +20,7 @@ export const authModule = {
}, },
mutations: { mutations: {
[SET_USER_INFO](state: any, user: User): void { [AUTH_MUTATIONS.SET_USER_INFO](state: any, user: User): void {
state.firstName = user.firstName; state.firstName = user.firstName;
state.lastName = user.lastName; state.lastName = user.lastName;
state.email = user.email; state.email = user.email;
@ -34,7 +33,7 @@ export const authModule = {
state.companyPostalCode = user.company.postalCode; state.companyPostalCode = user.company.postalCode;
}, },
[REVERT_TO_DEFAULT_USER_INFO](state: any): void { [AUTH_MUTATIONS.REVERT_TO_DEFAULT_USER_INFO](state: any): void {
state.firstName = ""; state.firstName = "";
state.lastName = ""; state.lastName = "";
state.email = ""; state.email = "";
@ -58,5 +57,5 @@ export const authModule = {
}; };
function setUserInfo({commit}: any, userInfo: User): void { function setUserInfo({commit}: any, userInfo: User): void {
commit(SET_USER_INFO, userInfo) commit(AUTH_MUTATIONS.SET_USER_INFO, userInfo)
} }

View File

@ -0,0 +1,44 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
import {
PROJECTS_MUTATIONS
} from "../mutationConstants";
import { createProject } from "@/utils/qraphql/createProjectsQuery";
export const projectsModule = {
state: {
projects: [],
selectedProject: {
name: "",
id: "",
}
},
mutations: {
[PROJECTS_MUTATIONS.CREATE](state: any, createdProject: Project): void {
state.projects.push(createdProject)
},
[PROJECTS_MUTATIONS.FETCH](state: any, projects: Project[]): void {
state.projects = projects
},
},
actions: {
fetchProjects: async function({commit}: any) {
commit(PROJECTS_MUTATIONS.FETCH, )
},
createProject: async function({commit}: any, project: Project) {
let response = createProject(project);
if(response) {
commit(PROJECTS_MUTATIONS.CREATE, response)
}
}
},
getters: {
},
};

View File

@ -1,5 +1,14 @@
// Copyright (C) 2018 Storj Labs, Inc. // Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information. // See LICENSE for copying information.
export const SET_USER_INFO = "SET_USER_INFO"; export const AUTH_MUTATIONS = {
export const REVERT_TO_DEFAULT_USER_INFO = "REVERT_TO_DEFAULT_USER_INFO"; SET_USER_INFO: "SET_USER_INFO",
REVERT_TO_DEFAULT_USER_INFO: "REVERT_TO_DEFAULT_USER_INFO",
};
export const PROJECTS_MUTATIONS = {
CREATE: "CREATE_PROJECT",
DELETE: "DELETE_PROJECT",
UPDATE: "UPDATE_PROJECT",
FETCH: "FETCH_PROJECTS"
};

10
web/satellite/src/types/projects.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
// Project is a type, used for creating new project in backend
declare type Project = {
name: string,
description: string
id: string,
isTermsAccepted: boolean,
}

View File

@ -4,13 +4,30 @@
import {HttpLink} from "apollo-link-http"; import {HttpLink} from "apollo-link-http";
import ApolloClient from "apollo-client/ApolloClient"; import ApolloClient from "apollo-client/ApolloClient";
import {InMemoryCache} from "apollo-cache-inmemory"; import {InMemoryCache} from "apollo-cache-inmemory";
import { setContext } from "apollo-link-context";
import { getToken } from "../utils/tokenManager"
// Satellite url
const satelliteUrl = new HttpLink({ const satelliteUrl = new HttpLink({
uri: 'http://192.168.1.90:8081/api/graphql/v0', uri: 'http://localhost:8081/api/graphql/v0',
}); });
// Adding auth headers
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = getToken();
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
});
// Creating apollo client
export default new ApolloClient({ export default new ApolloClient({
link: satelliteUrl, link: authLink.concat(satelliteUrl),
cache: new InMemoryCache(), cache: new InMemoryCache(),
connectToDevTools: true, connectToDevTools: true,
}); });

View File

@ -0,0 +1,37 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
import apollo from '../apolloManager';
import gql from "graphql-tag";
// TODO: all graphql queries should be totally refactored
// Performs graqhQL request.
// Throws an exception if error occurs
export async function createProject(project: Project): Promise<any> {
let response = apollo.mutate(
{
mutation: gql(`
mutation {
createProject(
input: {
name: "${project.name}",
description: "${project.description}",
isTermsAccepted: ${project.isTermsAccepted},
}
)
}`
),
fetchPolicy: "no-cache",
}
);
if(!response){
// TODO: replace with popup in future
console.log("cannot create user");
return null;
}
return response;
}