diff --git a/assets/img/logo.svg b/assets/img/logo.svg new file mode 100644 index 0000000..210f7ee --- /dev/null +++ b/assets/img/logo.svg @@ -0,0 +1 @@ +logo \ No newline at end of file diff --git a/assets/img/logo_webgem.svg b/assets/img/logo_webgem.svg new file mode 100644 index 0000000..eea1d0f --- /dev/null +++ b/assets/img/logo_webgem.svg @@ -0,0 +1 @@ +logo_webgem \ No newline at end of file diff --git a/assets/img/none-assigned.png b/assets/img/none-assigned.png new file mode 100644 index 0000000..fc1a31f Binary files /dev/null and b/assets/img/none-assigned.png differ diff --git a/assets/img/plus-circle.svg b/assets/img/plus-circle.svg new file mode 100644 index 0000000..f7a138c --- /dev/null +++ b/assets/img/plus-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/img/trash-alt.svg b/assets/img/trash-alt.svg new file mode 100644 index 0000000..6f2df79 --- /dev/null +++ b/assets/img/trash-alt.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/components/Redirect/index.js b/components/Redirect/index.js new file mode 100644 index 0000000..24c332b --- /dev/null +++ b/components/Redirect/index.js @@ -0,0 +1,12 @@ +import { Component } from 'preact'; +import { route } from 'preact-router'; + +export default class Redirect extends Component { + componentWillMount() { + route(this.props.to, true); + } + + render() { + return null; + } +} \ No newline at end of file diff --git a/components/addItem/index.js b/components/addItem/index.js new file mode 100644 index 0000000..6dd5a9c --- /dev/null +++ b/components/addItem/index.js @@ -0,0 +1,32 @@ +import { h, Component } from 'preact'; +import style from './style'; + +export default class AddItem extends Component{ + addItem(event) { + event.preventDefault(); + const timestamp = Date.now(); + const item = { + desc: this.name.value, + p1: `none`, + p2: `none`, + p3: `none`, + p4: `none`, + status: `To do` + }; + this.props.addItem(item); + this.addItemForm.reset(); + } + render() { + return ( +
+
(this.addItemForm = input)} class={style.newItemForm} onSubmit={(e) => this.addItem(e) }> + (this.name = input)} type="text" placeholder="Omschrijving van de opdracht" /> +
+
+ ); + } +} + +AddItem.propTypes = { + addItem: Component.PropTypes.func.isRequired +}; \ No newline at end of file diff --git a/components/addItem/style.css b/components/addItem/style.css new file mode 100644 index 0000000..047224d --- /dev/null +++ b/components/addItem/style.css @@ -0,0 +1,68 @@ +.newItem{ + display: flex; + align-items: center; + padding: 0; + margin: 0; + /* border-left: 7.5px solid #eee */ +} + +.newItem img{ + height: 18px; + margin: 5px +} + +.newItemForm{ + width: calc(100% - 1px); + min-width: 308px; + display: flex; + padding: 0; + margin: 0; + height: 35px; +} + +.newItemForm input{ + width: 100%; + background: none; + box-shadow: none; + outline: none; + border: none; + border-left: 7.5px solid #eee; + border-top: 1px solid #eee; + border-right: 1px solid #eee; + border-bottom: 1px solid #eee; + /* border-left: 7px solid ##c2c2c2; */ + font-size: 14px; + padding: 10px; + margin: 0; + color: #333; + transition: all .2s ease-in-out; + height: 35px; +} + +.newItemForm input::placeholder{ + color: #A09C9C; +} + +.newItemForm input:focus{ + border-color: #f44336; + color: #333; +} + +.newItemForm button{ + background: none; + outline: none; + border: none; + box-shadow: none; + border: 2px solid #d81b60; + font-size: 20px; + color: #d81b60; + padding: 0 10px; + display: flex; + align-items: center; + justify-content: center; +} + +.newItemForm button:hover{ + background: #d81b60; + color: #ffffff; +} \ No newline at end of file diff --git a/components/app.js b/components/app.js new file mode 100644 index 0000000..991f49e --- /dev/null +++ b/components/app.js @@ -0,0 +1,131 @@ +import { h, Component } from 'preact'; +import { Router } from 'preact-router'; + +import Header from './header'; +import Home from '../routes/home'; +import Profile from '../routes/profile'; +import Team from '../routes/team'; +import Login from '../routes/login'; +import Teams from '../routes/Teams'; + +import firebase from 'firebase/app'; +require('firebase/auth'); +import fireApp from '../base2'; + +const github = new firebase.auth.GithubAuthProvider(); +github.addScope('user:email'); + +const facebook = new firebase.auth.FacebookAuthProvider(); +facebook.addScope('user_email'); + +const twitter = new firebase.auth.TwitterAuthProvider(); + +export default class App extends Component { + constructor() { + super(); + // this.renderInventory = this.renderInventory.bind(this); + // this.renderLogin = this.renderLogin.bind(this); + this.authenticate = this.authenticate.bind(this); + this.logout = this.logout.bind(this); + this.authHandler = this.authHandler.bind(this); + // this.handleChange = this.handleChange.bind(this); + this.state = { + uid: null, + owner: null, + username: null, + userProfilePic: null + }; + } + + componentDidMount() { + firebase.auth(fireApp).onAuthStateChanged((user) => { + if (user) { + this.authHandler(null, { user }); + } + }); + // base.onAuth((user) => { + // if (user) { + // this.authHandler(null, { user }); + // } + // }); + } + + handleRoute = e => { + this.currentUrl = e.url; + }; + + authenticate(provider) { + console.log(`Trying to log in with ${provider}`); + firebase.auth(fireApp).signInWithPopup(provider).then(() => { + this.authHandler; + }); + // base.authWithOAuthPopup(provider, this.authHandler); + } + + logout() { + // base.unauth(); + firebase.auth(fireApp).signOut(); + this.setState({ uid: null, username: null, userProfilePic: null }); + } + + authHandler(err, authData) { + console.log(authData); + if (err) { + console.error(err); + return; + } + + // grab the database info + const globalRef = fireApp.database().ref('global'); + + // query the firebase once for the store data + globalRef.once('value', (snapshot) => { + const data = snapshot.val() || {}; + + // claim it as our own if there is no owner already + if(!data.owner) { + globalRef.set({ + owner: authData.user.uid + }); + } + + this.setState({ + uid: authData.user.uid, + owner: data.owner || authData.user.uid, + username: authData.user.displayName, + userProfilePic: authData.user.photoURL, + }); + }); + + } + + + render() { + return ( +
+
+ + + + + + + + + + +
+ ); + } +} diff --git a/components/boardLink/index.js b/components/boardLink/index.js new file mode 100644 index 0000000..1602f04 --- /dev/null +++ b/components/boardLink/index.js @@ -0,0 +1,37 @@ +import { h, Component } from 'preact'; +import { Link } from 'preact-router/match'; +import '@material/list/dist/mdc.list.min.css'; +import style from './style'; + +export default class BoardLink extends Component { + constructor() { + super(); + + this.areYouFuckingSure = this.areYouFuckingSure.bind(this); + } + + state = { + deletedBoard: 'boooooooi' + } + + areYouFuckingSure() { + let r = confirm(`You are about to delete ${this.props.details.name}. \nThis can't be undone! \nAre you sure you want to delete the board ${this.props.details.name}`); + if (r === true) { + this.props.removeBoard(this.props.index); + } + } + + render() { + const i = this.props; + return ( +
  • + + {i.details.name} + + + delete + +
  • + ); + } +} \ No newline at end of file diff --git a/components/boardLink/style.css b/components/boardLink/style.css new file mode 100644 index 0000000..9fa0d3f --- /dev/null +++ b/components/boardLink/style.css @@ -0,0 +1,18 @@ +nav a.activeBoard { + background: #9a67ea; + color: #ffffff; +} + +nav .boardLinkWrapper a.activeBoard { + background: #9a67ea; + color: #ffffff; +} + +nav a.activeBoard + a{ + background: #9a67ea; + color: #ffffff; +} + +.mdc-list-item__end-detail{ + flex: 0 1 5%; +} \ No newline at end of file diff --git a/components/boardcard/index.js b/components/boardcard/index.js new file mode 100644 index 0000000..6cb1c48 --- /dev/null +++ b/components/boardcard/index.js @@ -0,0 +1,37 @@ +import { h, Component } from 'preact'; +import style from './style'; +import Card from 'preact-material-components/Card'; +import { Link } from 'preact-router'; +import 'preact-material-components/Card/style.css'; +import 'preact-material-components/Button/style.css'; + +export default class BoardCard extends Component { + constructor() { + super(); + + const colors = ['#F47373', '#697689', '#37D67A', '#2CCCE4', '#ff8a65', '#ba68c8', '#e91e63', '#673ab7', '#3f51b5', '#2196f3', '#009688', '#ffc107']; + this.state = { + background: colors[Math.floor(12 * Math.random())] + }; + } + render() { + return ( +
    + + +
    + {this.props.boardname.name} +
    +
    + + + + show + + + +
    +
    + ); + } +} diff --git a/components/boardcard/style.css b/components/boardcard/style.css new file mode 100644 index 0000000..f40a75b --- /dev/null +++ b/components/boardcard/style.css @@ -0,0 +1,38 @@ +.cardWrap{ + display: inline-block; + width: 20%; + min-width: 300px; + margin: 10px 5px; +} + +.cardWrap h1{ + font-size: 25px; + text-transform: uppercase; + color: white; +} + +.cardBackcolor{ + background: #333; + color: white; + margin-left: -16px; + margin-top: -16px; + margin-right: -16px; + margin-bottom: -16px; + height: 175px; + display: flex; + justify-content: center; + align-items: center; + padding: 16px; +} + +.cardWrap button{ + width: 100%; +} + +.linkCard{ + width: 100%; + width: calc(50% - 8px); + margin-right: 8px; +} + +/* fix the button margin */ \ No newline at end of file diff --git a/components/confirmLeave/index.js b/components/confirmLeave/index.js new file mode 100644 index 0000000..bbfb464 --- /dev/null +++ b/components/confirmLeave/index.js @@ -0,0 +1,39 @@ +import { h, Component } from 'preact'; +import Dialog from 'preact-material-components/Dialog'; +import Button from 'preact-material-components/Button'; +import 'preact-material-components/List/style.css'; +import 'preact-material-components/Button/style.css'; +import 'preact-material-components/Dialog/style.css'; +import style from './style.css'; + +export default class ConfirmLeave extends Component { + constructor() { + super(); + this.handleAccept = this.handleAccept.bind(this); + } + + handleAccept() { + this.props.handleLeave(this.props.team); + } + render(){ + return ( +
    + + {this.scrollingDlg=scrollingDlg;}} onAccept={this.handleAccept}> + +

    You are about to leave the team {this.props.team}!

    +

    Are you sure you want to leave {this.props.team}?

    +
    + + cancel + leave team + +
    +
    + ); + } +} \ No newline at end of file diff --git a/components/confirmLeave/style.css b/components/confirmLeave/style.css new file mode 100644 index 0000000..8cffcf8 --- /dev/null +++ b/components/confirmLeave/style.css @@ -0,0 +1,5 @@ +.leaveButton{ + width: calc(50% - 8px); + margin-right: 8px; + margin-left: 8px; +} \ No newline at end of file diff --git a/components/createTeam/index.js b/components/createTeam/index.js new file mode 100644 index 0000000..e3a2d44 --- /dev/null +++ b/components/createTeam/index.js @@ -0,0 +1,37 @@ +import { h, Component } from 'preact'; +import style from './style'; +import Card from 'preact-material-components/Card'; +import 'preact-material-components/Card/style.css'; + +export default class CreateTeam extends Component { + createTeam(event) { + event.preventDefault(); + const team = { + name: this.name.value, + members: { + [this.props.uuid]: true + } + }; + this.props.addTeam(team); + this.createTeamForm.reset(); + } + render() { + return ( +
    + + +
    + new team +
    +
    + +
    (this.createTeamForm = input)} class={style.newTeamForm} onSubmit={(e) => this.createTeam(e)}> + (this.name = input)} type="text" placeholder="team name" /> + +
    +
    +
    +
    + ); + } +} \ No newline at end of file diff --git a/components/createTeam/style.css b/components/createTeam/style.css new file mode 100644 index 0000000..eae13b1 --- /dev/null +++ b/components/createTeam/style.css @@ -0,0 +1,77 @@ +.cardWrap{ + display: inline-block; + width: 20%; + min-width: 300px; + margin: 10px 5px; +} + +.cardWrap h1{ + font-size: 25px; + text-transform: uppercase; + color: white; +} + +.cardBackcolor{ + background: #9c27b0; + color: white; + margin-left: -16px; + margin-top: -16px; + margin-right: -16px; + margin-bottom: -16px; + height: 175px; + display: flex; + justify-content: center; + align-items: center; + padding: 16px; +} + +.buttonSubmit{ + font-family: Roboto, sans-serif; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-size: 0.875rem; + font-weight: 500; + letter-spacing: 0.04em; + line-height: 2.25rem; + text-decoration: none; + text-transform: uppercase; + display: inline-block; + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; + min-width: 88px; + height: 36px; + padding: 0 16px; + border: none; + outline: none; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-appearance: none; + overflow: hidden; + vertical-align: middle; + border-radius: 2px; + color: white; +} + +.teamInput{ + height: 35px; + width: 142px; + margin-left: 0px; + margin-right: 8px; + padding: 5px 10px; + border: none; + border-bottom: 2px solid #eee; + background: none; + color: #333; + font-size: 15px; +} + +.teamInput:focus{ + border: none; + outline: none; + border-bottom: 2px solid #9c27b0; + color: #9c27b0; +} \ No newline at end of file diff --git a/components/data/index.js b/components/data/index.js new file mode 100644 index 0000000..ec595f2 --- /dev/null +++ b/components/data/index.js @@ -0,0 +1,144 @@ +const personen = [ + { + title: 'MV', + personen: [ + { + name: 'Yannick Frisart', + functie: 'Front-end', + img: 'https://20578.tk/panel/img/test.png' + }, + { + name: 'Thijs van Rijn', + functie: 'Designer', + img: 'https://20578.tk/panel/img/test.png' + } + ] + }, + { + title: 'MD', + personen: [ + { + name: 'Rick Woltheus', + functie: 'Back-end', + img: 'https://20578.tk/panel/img/test.png' + }, + { + name: 'Coen Filipsen', + functie: 'Communicatie', + img: 'https://20578.tk/panel/img/test.png' + } + ] + }, + { + title: 'Stagiairs', + personen: [ + { + name: 'Maurice', + functie: 'Stagair', + img: 'https://20578.tk/panel/img/test.png' + }, + { + name: 'Patrick', + functie: 'Stagair', + img: 'https://20578.tk/panel/img/test.png' + } + ] + } +]; + +// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters +function escapeRegexCharacters(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function getSuggestions(value) { + const escapedValue = escapeRegexCharacters(value.trim()); + + if (escapedValue === '') { + return []; + } + + const regex = new RegExp('^' + escapedValue, 'i'); + + return personen + .map(section => { + return { + title: section.title, + personen: section.personen.filter(persoon => regex.test(persoon.name)) + }; + }) + .filter(section => section.personen.length > 0); +} + +function getSuggestionValue(suggestion) { + return suggestion.name; +} + +function renderSuggestion(suggestion) { + return ( + {suggestion.name} + ); +} + +function renderSectionTitle(section) { + return ( + {section.title} + ); +} + +function getSectionSuggestions(section) { + return section.personen; +} + +class App extends React.Component { + constructor() { + super(); + + this.state = { + value: '', + suggestions: [] + }; + } + + onChange = (event, { newValue, method }) => { + this.setState({ + value: newValue + }); + }; + + onSuggestionsFetchRequested = ({ value }) => { + this.setState({ + suggestions: getSuggestions(value) + }); + }; + + onSuggestionsClearRequested = () => { + this.setState({ + suggestions: [] + }); + }; + + render() { + const { value, suggestions } = this.state; + const inputProps = { + placeholder: "Type 'c'", + value, + onChange: this.onChange + }; + + return ( + + ); + } +} + +ReactDOM.render(, document.getElementById('app')); diff --git a/components/data/style.css b/components/data/style.css new file mode 100644 index 0000000..e69de29 diff --git a/components/groups/index.js b/components/groups/index.js new file mode 100644 index 0000000..81e48b2 --- /dev/null +++ b/components/groups/index.js @@ -0,0 +1,154 @@ +import { h, Component } from 'preact'; +import base from '../../base'; + +import Item from '../item'; +import AddItem from '../addItem'; + +import '@material/button/dist/mdc.button.min.css'; +import style from './style'; +// import dist from 'react-autosuggest'; +import { BlockPicker } from 'react-color'; + +export default class BoardGroup extends Component { + constructor(props) { + super(props); + + this.addItem = this.addItem.bind(this); + this.updateItem = this.updateItem.bind(this); + this.removeItem = this.removeItem.bind(this); + this.handleDeleteGroupItems = this.handleDeleteGroupItems.bind(this); + this.handleColorChange = this.handleColorChange.bind(this); + + this.state = { + items: {}, + displayColorPicker: false, + color: this.props.details.color + }; + } + + componentWillMount(nextProps) { + this.ref = base.syncState(`/items/${this.props.index}/`, { + context: this, + state: 'items' + }); + } + + componentWillUnmount() { + base.removeBinding(this.ref); + } + + addItem(item) { + // update our state + const items = { ...this.state.items }; + // add in our new fish + const timestamp = Date.now(); + items[`item_${timestamp}`] = item; + // set state + this.setState({ items }); + } + + updateItem(key, updatedOpdracht) { + const items = { ...this.state.items }; + items[key] = updatedOpdracht; + this.setState({ items }); + } + + handleChange(e, key) { + const group = this.props.details; + const updatedGroup = { + ...group, + [e.target.name]: e.target.value + }; + this.props.updateGroup(key, updatedGroup); + } + + removeItem = (key) => { + const items = { ...this.state.items }; + items[key] = null; + this.setState({ items }); + }; + + handleColorClick = () => { + this.setState({ displayColorPicker: !this.state.displayColorPicker }); + }; + + handleColorClose = () => { + this.setState({ displayColorPicker: false }); + }; + + handleColorChange = (color) => { + this.setState({ color: color.hex }); + const group = this.props.details; + const updatedGroup = { + ...group, + color: this.state.color + }; + this.props.updateGroup(this.props.index, updatedGroup); + }; + + handleDeleteGroupItems(){ + base.remove(`/items/${this.props.index}`); + this.props.removeGroup(this.props.index); + } + + render() { + const details = this.props.details; + return ( +
    + + + + + + + + + + + + + + + + + { + Object + .keys(this.state.items) + .map(key => ) + } + + + + + + +
    + this.handleChange(e, this.props.index)} type="text" value={details.name} placeholder="This is a example header (Click to edit)" /> + Door wiep2p3p4statusdoeldeadlinehoelang erover gedaandelete
    + + + +
    +
    +
    + group color +
    +
    + {this.state.displayColorPicker ?
    +
    + +
    : null} +
    +
    + +
    +
    + ); + } +} diff --git a/components/groups/style.css b/components/groups/style.css new file mode 100644 index 0000000..3906950 --- /dev/null +++ b/components/groups/style.css @@ -0,0 +1,119 @@ +.groupWrap{ + width: 100%; + padding: 15px; + position: relative; +} + +.deleteItem{ + position: absolute; + right: 2px; + display: none; + background:none; + border: none; + outline: none; + border: 1px solid #cecece; + border-radius: 3px; +} + +.groupWrap:hover .deleteItem{ + display: block; +} + +.group{ + width: 100%; + display: table; +} + +.group thead{ + width: 100%; + padding: 2px 0 6px 7.5px; + display: block; +} + +.group thead tr{ + display: table-row; + /* width: 100%; */ +} + +.group thead th{ + text-align: center; + text-overflow: ellipsis; + /* overflow: hidden; */ + white-space: nowrap; + font-size: 13px; + font-weight: 300; + /* margin: 0 1px; */ + /* padding: 0 0px; */ + display: table-cell; + width: 10%; +} + +.group tbody{ + width: 100%; +} + +.inputHeader{ + background: none; + border: none; + outline: none; + text-align: left; + font-size: 18px; + width: 100%; + min-width: 300px; + margin: 0 1px 0 0; + border: 0.5px dotted transparent; +} + +.inputHeader:focus{ + border: 0.5px dotted #eee; +} + +.color { + height: 36px; + border-radius: 2px; + background: deeppink; + width: 100%; + display: block; +} + +.swatch { + padding: 0px; + background: #fff; + border-radius: 2px; + /* box-shadow: 0 0 0 1px rgba(0,0,0,.1); */ + cursor: pointer; + color: white; + display: block; + text-align: center; + line-height: 35px; + box-shadow: 0 2px 4px -1px rgba(0,0,0,.2), 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12); +} + +.popover { + position: absolute; + z-index: 2; + top: 40px; + left: 0; + border-radius: 6px; + width: 100%; + box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12); +} + +.cover { + position: fixed; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.wrapColor{ + display: inline-block; + position: relative; + width: 100%; + margin-left: 0px; +} + +.tdAddItem{ + padding: 0; +} \ No newline at end of file diff --git a/components/header/index.js b/components/header/index.js new file mode 100644 index 0000000..0f4b1e1 --- /dev/null +++ b/components/header/index.js @@ -0,0 +1,47 @@ +import { h, Component } from 'preact'; +import { Link } from 'preact-router/match'; +import userMenu from '../user'; +import style from './style'; + +import logo from '../../assets/img/logo.svg'; +import UserMenu from '../user/index'; + +export default class Header extends Component { + constructor() { + super(); + this.renderLoggedInNav = this.renderLoggedInNav.bind(this); + } + + renderLoggedInNav() { + return ( +
    + +
    + ); + } + render() { + if (this.props.uid !== null) { + return
    {this.renderLoggedInNav()}
    ; + } + + return ( +
    +
    + +
    +
    + ); + } +} diff --git a/components/header/style.css b/components/header/style.css new file mode 100644 index 0000000..cdd94a3 --- /dev/null +++ b/components/header/style.css @@ -0,0 +1,79 @@ +/* https://material.io/color/#!/?view.left=0&view.right=0&primary.color=F5F5F5&secondary.color=D81B60 */ +.header { + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 50px; + padding: 0; + background: #9c27b0; + color: white; + z-index: 50; + display: flex; + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25); +} + +.headWrap{ + height: 100%; + display: inline-block; + position: relative; + flex: 1 1 75%; +} + +.headWrap nav { + float: right; + font-size: 100%; +} + +.headWrap nav a { + display: inline-block; + height: 50px; + line-height: 50px; + padding: 0 15px; + min-width: 50px; + text-align: center; + background: rgba(255,255,255,0); + text-decoration: none; + color: #fff; + vertical-align: top; +} + +/* .headWrap nav a div{ + width: 100%; +} */ + +.headWrap nav a.boooooooi { + padding: 0; +} + +.headWrap nav a.boooooooi div { + width: 50px; +} + +.headWrap nav a div div { + top: 50px !important; +} + +.headWrap nav a div div ul li p{ + padding-left: 15px; +} + +.headWrap nav a div div ul li{ + transition: all .3s ease; + background: #fff; +} + +.headWrap nav a div div ul li:hover{ + background-color: #f3f3f3; +} + +.headWrap nav a:hover, +.headWrap nav a:active { + background: #9a67ea; + color: #fff +} + +.header nav a.active { + background: #320b86; + color: #ffffff; +} \ No newline at end of file diff --git a/components/item/index.js b/components/item/index.js new file mode 100644 index 0000000..168ce64 --- /dev/null +++ b/components/item/index.js @@ -0,0 +1,149 @@ +import { h, Component } from 'preact'; +import style from './style'; +import SelectPerson from '../selectPerson'; +import SelectStatus from '../selectStatus'; + +import trashIcon from '../../assets/img/trash-alt.svg'; + +export default class Item extends Component { + handleChange(e, key) { + const item = this.props.details; + const updatedOpdracht = { + ...item, + [e.target.name]: e.target.value + }; + this.props.updateItem(key, updatedOpdracht); + } + + + render() { + const details = this.props.details; + const index = this.props.index; + return ( + + +
    + this.handleChange(e, this.props.index)} + type="text" + value={details.desc} + placeholder="This is a example description (Click to edit)" + /> +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + this.handleChange(e, this.props.index)} + type="date" + value={details.finishGoal} + placeholder="dd/mm/yyyy" + /> +
    + + +
    + this.handleChange(e, this.props.index)} + type="date" + value={details.deadline} + placeholder="dd/mm/yyyy" + /> +
    + + +
    + this.handleChange(e, this.props.index)} + type="number" + value={details.tooktime} + placeholder="000" + /> +
    + + +
    + +
    + + + ); + } +} \ No newline at end of file diff --git a/components/item/style.css b/components/item/style.css new file mode 100644 index 0000000..bc9eb1f --- /dev/null +++ b/components/item/style.css @@ -0,0 +1,106 @@ +tbody{ + display: table-row-group +} + +tbody tr.tableRow{ + /* width: 100%; */ + display: table-row; +} + +tbody tr.tableRow td{ + margin: 1px; + border-bottom: 1px solid #e6e6e6; + white-space: nowrap; + padding: 0; + width: 10%; + display: table-cell; +} + +.innerWrap{ + background: #f0f0f0; + padding: 0; + height: 35px; + color: #596496; + display: block; + text-decoration: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + outline: none; + width: auto; + font-size: 13px; +} + +.innerWrap input{ + background: none; + width: 100%; + height: 100%; + border: none; + outline: none; + box-shadow: none; + padding: 5px 10px; + display: flex; + color: #596496; + font-size: 13px; + font-family: Arial, Helvetica, sans-serif; + text-align: center; +} + +.innerWrap input.inputDesc{ + /* width: 20vw; */ + min-width: 250px; + text-align: left; +} + +.perwrap { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.personimg{ + height: 70%; +} + +.suggestionContent { + display: flex; + align-items: center; + background-repeat: no-repeat; +} + +button.deleteItem{ + background: none; + outline: none; + border: none; + border-radius: none; + box-shadow: none; + display: block; + width: 100%; + height: 35px; + padding: 3px; + margin: 0; +} + +button.deleteItem img{ + height: 25px; +} + +input.deadline{ + padding: 2px; +} + +/* input.deadline::before{ + content: ''; +} */ +input.deadline::-webkit-datetime-edit-year-field:not([aria-valuenow]), +input.deadline::-webkit-datetime-edit-month-field:not([aria-valuenow]), +input.deadline::-webkit-datetime-edit-day-field:not([aria-valuenow]) { + color: transparent; +} + +/* ::-webkit-datetime-edit-month-field { color: blue; } +::-webkit-datetime-edit-day-field { color: green; } +::-webkit-datetime-edit-year-field { color: purple; } */ \ No newline at end of file diff --git a/components/newBoard/index.js b/components/newBoard/index.js new file mode 100644 index 0000000..3c6922f --- /dev/null +++ b/components/newBoard/index.js @@ -0,0 +1,27 @@ +import { h, Component } from 'preact'; +import style from './style'; + +export default class NewBoard extends Component{ + createBoard(event) { + event.preventDefault(); + const board = { + name: this.name.value, + }; + this.props.addBoard(board); + this.addBoardForm.reset(); + } + render() { + return ( +
    +
    (this.addBoardForm = input)} class={style.newboardForm} onSubmit={(e) => this.createBoard(e) }> + (this.name = input)} type="text" placeholder="board name" /> + +
    +
    + ); + } +} + +NewBoard.propTypes = { + addBoard: Component.PropTypes.func.isRequired +}; \ No newline at end of file diff --git a/components/newBoard/style.css b/components/newBoard/style.css new file mode 100644 index 0000000..90ac371 --- /dev/null +++ b/components/newBoard/style.css @@ -0,0 +1,67 @@ +.newboard{ + display: flex; + align-items: center; + padding: 10px; +} + +.newboard img{ + height: 18px; + margin: 5px +} + +.newboardForm{ + width: 100%; + display: flex; + padding: 0; + margin: 0; +} + +.newboardForm input{ + width: 90%; + background: none; + box-shadow: none; + outline: none; + border: none; + border-bottom: 2px solid #A09C9C; + font-size: 17px; + padding: 10px; + margin: 0; + color: #333; + transition: all .2s ease-in-out; +} + +.newboardForm input::placeholder{ + color: #A09C9C; +} + +.newboardForm button{ + background: none; + outline: none; + border: none; + box-shadow: none; + /* border: 2px solid #f44336; */ + border-bottom: 2px solid #A09C9C; + font-size: 20px; + color: #ba000d; + padding: 0 10px; + display: flex; + align-items: center; + justify-content: center; + transition: all .2s ease; +} + +.newboardForm input:focus{ + border-color: #ff7961; + color: #f44336; +} + + +.newboardForm input:focus + button{ + border-color: #ff7961; +} + +.newboardForm button:hover{ + background: #ff7961; + color: #fafafa; + border-color: #ff7961; +} \ No newline at end of file diff --git a/components/newGroup/index.js b/components/newGroup/index.js new file mode 100644 index 0000000..d6cee0e --- /dev/null +++ b/components/newGroup/index.js @@ -0,0 +1,66 @@ +import { h, Component } from 'preact'; +import { BlockPicker } from 'react-color'; +import Textfield from 'preact-material-components/Textfield'; +import 'preact-material-components/Textfield/style.css'; +import style from './style'; + +export default class NewGroup extends Component { + state = { + displayColorPicker: false, + color: '#f47373' + } + + // handleTest = (event) => { + // console.log(this.value); + // } + + createGroup(event) { + event.preventDefault(); + const group = { + name: this.name.value, + color: this.state.color + }; + this.props.addGroup(group); + this.addGroupForm.reset(); + } + + handleClick = () => { + this.setState({ displayColorPicker: !this.state.displayColorPicker }); + }; + + handleClose = () => { + this.setState({ displayColorPicker: false }); + }; + + handleChange = (color) => { + this.setState({ color: color.hex }); + }; + + render() { + return ( +
    +

    new group

    +
    (this.addGroupForm = input)} class={style.newGroupForm} onSubmit={(e) => this.createGroup(e)}> + (this.name = input)} type="text" placeholder="Name of the new group" /> +
    +
    +
    + group color +
    +
    + {this.state.displayColorPicker ?
    +
    + +
    : null} +
    + +
    + ); + } +} \ No newline at end of file diff --git a/components/newGroup/style.css b/components/newGroup/style.css new file mode 100644 index 0000000..1b83f37 --- /dev/null +++ b/components/newGroup/style.css @@ -0,0 +1,94 @@ +.newGroup{ + display: block; + width: 100%; + padding: 0 17px; +} + +.newGroupForm{ + width: 100%; + display: flex; +} + +.newGroupForm input{ + width: 90%; + background: none; + -webkit-box-shadow: none; + box-shadow: none; + outline: none; + border: none; + border-left: 7.5px solid #eee; + border-top: 1px solid #eee; + border-right: 1px solid #eee; + border-bottom: 1px solid #eee; + font-size: 14px; + padding: 10px; + margin: 0; + color: #333; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; + height: 35px; +} + +.newGroupForm input:focus{ + border-color: #f44336; + color: #333; +} + +.newGroupH3{ + margin: 0; + padding: 0; +} + +.color { + height: 36px; + border-radius: 2px; + background: deeppink; + width: 100%; + display: block; +} + +.swatch { + padding: 0px; + background: #fff; + border-radius: 2px; + /* box-shadow: 0 0 0 1px rgba(0,0,0,.1); */ + cursor: pointer; + color: white; + display: block; + text-align: center; + line-height: 35px; + box-shadow: 0 2px 4px -1px rgba(0,0,0,.2), 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12); +} + +.popover { + position: absolute; + z-index: 2; + top: 40px; + left: 0; + border-radius: 6px; + width: 100%; + box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12); +} + +.cover { + position: fixed; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.wrapColor{ + display: inline-block; + position: relative; + width: 15%; + margin-left: 2px; +} + +.tdAddItem{ + padding: 0; +} + +h3{ + font-weight: 400; +} \ No newline at end of file diff --git a/components/selectPerson/index.js b/components/selectPerson/index.js new file mode 100644 index 0000000..151c502 --- /dev/null +++ b/components/selectPerson/index.js @@ -0,0 +1,234 @@ +import { h, Component } from 'preact'; +import Autosuggest from 'react-autosuggest'; + +const uuidv4 = require('uuid/v4'); + +import base from '../../base'; +import style from './style'; + +const personen = [ + { + title: 'MV', + personen: [ + { + name: 'Yannick Frisart', + functie: 'Front-end', + img: 'https://20578.tk/board/user-profilepics/yannick.png' + }, + { + name: 'Thijs van Rijn', + functie: 'Designer', + img: 'https://20578.tk/board/user-profilepics/thijs.png' + } + ] + }, + { + title: 'MD', + personen: [ + { + name: 'Rick Woltheus', + functie: 'Back-end', + img: 'https://20578.tk/board/user-profilepics/rick.png' + }, + { + name: 'Coen Filipsen', + functie: 'Communicatie', + img: 'https://20578.tk/board/user-profilepics/coen.png' + } + ] + }, + { + title: 'Stagiairs', + personen: [ + { + name: 'Maurice', + functie: 'Stagair', + img: 'https://20578.tk/board/user-profilepics/none.png' + }, + { + name: 'Patrick', + functie: 'Stagair', + img: 'https://20578.tk/board/user-profilepics/none.png' + } + ] + }, + { + title: 'Clear', + personen: [ + { + name: '', + functie: 'reset', + img: 'https://20578.tk/board/user-profilepics/none.png' + } + ] + } +]; + + +// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters +function escapeRegexCharacters(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function getSuggestions(value) { + const escapedValue = escapeRegexCharacters(value.trim()); + + if (escapedValue === '') { + return personen; + } + + // const regex = new RegExp('^' + escapedValue, 'i'); + + return personen; +} + +function getSuggestionValue(suggestion) { + return suggestion.name; +} + +function renderSuggestion(suggestion) { + return ( +
    + {suggestion.name} + {suggestion.name} + {suggestion.functie} +
    + ); +} + +function renderSectionTitle(section) { + return ( + {section.title} + ); +} + +function getImg(newValue) { + if (newValue === 'Yannick Frisart') { + return 'https://20578.tk/board/user-profilepics/yannick.png'; + } + else if (newValue === 'Thijs van Rijn') { + return 'https://20578.tk/board/user-profilepics/thijs.png'; + } + else if (newValue === 'Rick Woltheus') { + return 'https://20578.tk/board/user-profilepics/rick.png'; + } + else if (newValue === 'Coen Filipsen') { + return 'https://20578.tk/board/user-profilepics/coen.png'; + } + else if (newValue === 'Maurice') { + return 'https://20578.tk/board/user-profilepics/none.png'; + } + else if (newValue === 'Patrick') { + return 'https://20578.tk/board/user-profilepics/none.png'; + } + return 'https://20578.tk/board/user-profilepics/none.png'; +} + +function getSectionSuggestions(section) { + return section.personen; +} + +// const renderInputComponent = inputProps => ( +//
    +// +// {/*
    +//
    */} +// +//
    +// ); + +export default class SelectPerson extends Component { + constructor(props) { + super(props); + // Autosuggest is a controlled component. + // This means that you need to provide an input value + // and an onChange handler that updates this value (see below). + // Suggestions also need to be provided to the Autosuggest, + // and they are initially empty because the Autosuggest is closed. + + this.state = { + value: '', + imgProfilePic: 'https://20578.tk/board/user-profilepics/none.png', + suggestions: [], + randomKey: '' + }; + } + + componentWillMount(nextProps) { + base.fetch(`/items/${this.props.groupI}/${this.props.index}/${this.props.personI}`, { + context: this, + then(data){ + this.setState({ + value: data, + imgProfilePic: getImg(data) + }); + } + }); + this.setState({ + randomKey: uuidv4() + }); + } + + onChange = (event, { newValue, method }) => { + this.setState({ + value: newValue, + imgProfilePic: getImg(newValue) + }); + const item = this.props.details; + const updatedOpdracht = { + ...item, + [this.props.personI]: newValue + }; + this.props.updateItem(this.props.index, updatedOpdracht); + }; + + onSuggestionsFetchRequested = ({ value }) => { + this.setState({ + suggestions: getSuggestions(value) + }); + }; + + onSuggestionsClearRequested = () => { + this.setState({ + suggestions: [] + }); + }; + + + render() { + const { value, suggestions } = this.state; + const inputProps = { + placeholder: 'Asign a person', + value, + onChange: this.onChange + }; + + const renderInputComponent = inputProps => ( +
    + + {/*
    +
    */} + +
    + ); + + // Finally, render it! + return ( + + ); + } +} \ No newline at end of file diff --git a/components/selectPerson/style.css b/components/selectPerson/style.css new file mode 100644 index 0000000..8ceb077 --- /dev/null +++ b/components/selectPerson/style.css @@ -0,0 +1,159 @@ +.container { + position: relative; + display: block; + width: 100%; + height: 100%; +} + +input.input { + /* width: 100%; + height: 100%; */ + display: inline-block; + /* padding: 10px 20px; */ + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border: none; + padding: 0; + /* border: 1px solid #aaa; */ + /* border-radius: 4px; */ + width: 0px; + height: 0px; +} + +input.inputFocused { + outline: none; + /* width: 100%; + height: 100%; + padding: 10px 20px; */ +} + +/* input.inputFocused + label{ + display: none; +} */ + +input.inputOpen { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.suggestionsContainer { + display: none; +} + +.suggestionsContainerOpen { + display: block; + position: absolute; + top: 36px; + width: 280px; + height: auto; + background-color: #fafafa; + box-shadow: 0px 3px 3px -2px rgba(0, 0, 0, 0.2), 0px 3px 4px 0px rgba(0, 0, 0, 0.14), 0px 1px 8px 0px rgba(0, 0, 0, 0.12); + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + z-index: 2; +} + +.suggestionsList { + margin: 0; + padding: 0; + list-style-type: none; + display: block; +} + +.suggestion { + cursor: pointer; + padding: 10px 20px; + display: block +} + +.suggestionHighlighted { + background-color: #ddd; +} + +.sectionContainer { + border-top: 1px dashed #ccc; + display: block; +} + +.sectionContainerFirst { + border-top: 0; + display: block; +} + +.sectionTitle { + padding: 10px 0 0 10px; + font-size: 12px; + color: #777; + display: block +} + +.suggestionName{ + font-size: 16px; + color: #596496; + flex: 1 0; +} + +.suggestionFunctie{ + font-size: 12px; + vertical-align: bottom; +} + +.suggestionWrapper{ + display: flex; + width: 100%; + height: 100%; + justify-content: flex-start; + align-items: center; +} + +.assignedOpdrachtPic{ + height: 30px; + margin: 0 15px 0 0; +} + +.inputPersonAssign{ + position: relative; + width: 100%; + display: block; + height: 100%; +} + +input.inputFieldPersonAssign{ + width: 0px; + height: 0px; + appearance: none; +} + +.inputPersonAssignWrap{ + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.inputPersonAssignWrap:focus{ + display: none; +} + +.inputPersonAssignImg{ + height: 80%; +} + +/* .wrapInputBoi{ + width: 0px; + height: 0px; + overflow: hidden; +} */ + +.wrapInputBoi input:focus{ + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/components/selectStatus/index.js b/components/selectStatus/index.js new file mode 100644 index 0000000..d5c5efd --- /dev/null +++ b/components/selectStatus/index.js @@ -0,0 +1,223 @@ +import { h, Component } from 'preact'; +import Autosuggest from 'react-autosuggest'; + +const uuidv4 = require('uuid/v4'); + +import base from '../../base'; +import style from './style'; + +const statusses = [ + { + title: 'Done', + statusses: [ + { + name: 'Done', + color: '#00c875' + }, + { + name: 'Ready for testing', + color: '#0086c0' + } + ] + }, + { + title: 'In process', + statusses: [ + { + name: 'Working on it', + color: '#fdab3d' + }, + { + name: 'Work needed (rejected)', + color: '#a25ddc' + }, + { + name: `Need help (I'm stuck)`, + color: '#e2445c' + } + ] + }, + { + title: 'To do', + statusses: [ + { + name: 'To do', + color: '#c4c4c4' + }, + { + name: 'Cancelled', + color: '#a00037' + } + ] + } +]; + +// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters +function escapeRegexCharacters(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function getSuggestions(value) { + // const escapedValue = escapeRegexCharacters(value.trim()); + + return statusses; + + // const regex = new RegExp('^' + escapedValue, 'i'); + + // return statusses + // .map(section => { + // return { + // title: section.title, + // statusses: section.statusses.filter(status => regex.test(status.name)), + // color: section.statusses.filter(status => regex.test(status.color)) + // }; + // }) + // .filter(section => section.statusses.length > 0); +} + +function getSuggestionValue(suggestion) { + return suggestion.name; +} + +function renderSuggestion(suggestion) { + return ( +
    + {suggestion.name} +
    + ); +} + +function renderSectionTitle(section) { + return ( + {section.title} + ); +} + +function getColor(newValue) { + if (newValue === 'Done') { + return '#00c875'; + } + else if (newValue === 'Ready for testing') { + return '#0086c0'; + } + else if (newValue === 'Working on it') { + return '#fdab3d'; + } + else if (newValue === 'Work needed (rejected)') { + return '#a25ddc'; + } + else if (newValue === `Need help (I'm stuck)`) { + return '#e2445c'; + } + else if (newValue === 'To do') { + return '#c4c4c4' + } + else if (newValue === 'Cancelled') { + return '#a00037' + } +} + +function getSectionSuggestions(section) { + return section.statusses; +} + +// const renderInputComponent = inputProps => ( +//
    +// +// {/*
    +//
    */} +// +//
    +// ); + +export default class SelectStatus extends Component { + constructor(props) { + super(props); + // Autosuggest is a controlled component. + // This means that you need to provide an input value + // and an onChange handler that updates this value (see below). + // Suggestions also need to be provided to the Autosuggest, + // and they are initially empty because the Autosuggest is closed. + + this.state = { + value: '', + color: '#c4c4c4', + suggestions: [], + randomKey: '' + }; + getColor(this.state.value); + } + + componentWillMount(nextProps) { + base.fetch(`/items/${this.props.groupI}/${this.props.index}/status`, { + context: this, + then(data){ + this.setState({ + value: data, + color: getColor(data) + }); + } + }); + this.setState({ + randomKey: uuidv4() + }) + } + + onChange = (event, { newValue, method }) => { + this.setState({ + value: newValue, + color: getColor(newValue) + }); + const item = this.props.details; + const updatedOpdracht = { + ...item, + [this.props.statusI]: newValue + }; + this.props.updateItem(this.props.index, updatedOpdracht); + }; + + onSuggestionsFetchRequested = ({ value }) => { + this.setState({ + suggestions: getSuggestions(value) + }); + }; + + onSuggestionsClearRequested = () => { + this.setState({ + suggestions: [] + }); + }; + + + render() { + const { value, suggestions } = this.state; + const inputProps = { + placeholder: 'Asign a status', + value, + onChange: this.onChange + }; + + const renderInputComponent = inputProps => ( + + ); + + // Finally, render it! + return ( + + ); + } +} \ No newline at end of file diff --git a/components/selectStatus/style.css b/components/selectStatus/style.css new file mode 100644 index 0000000..17c18af --- /dev/null +++ b/components/selectStatus/style.css @@ -0,0 +1,111 @@ +.container { + position: relative; + display: block; + width: 100%; + height: 100%; +} + +input.input { + width: 100%; + height: 100%; + display: inline-block; + padding: 10px 20px; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border: none; + color: white; + /* padding: 0; */ + /* border: 1px solid #aaa; */ + /* border-radius: 4px; */ + /* width: 0px; */ + /* height: 0px; */ +} + +input.input::placeholder{ + color: white; +} + +input.inputFocused { + outline: none; + width: 100%; + height: 100%; + padding: 10px 20px; +} + +input.inputOpen { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.suggestionsContainer { + display: none; +} + +.suggestionsContainerOpen { + display: block; + position: absolute; + top: 36px; + width: 280px; + background-color: #fafafa; + box-shadow: 0px 3px 3px -2px rgba(0, 0, 0, 0.2), 0px 3px 4px 0px rgba(0, 0, 0, 0.14), 0px 1px 8px 0px rgba(0, 0, 0, 0.12); + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + z-index: 2; +} + +.suggestionsList { + margin: 0; + padding: 0; + list-style-type: none; + display: block; +} + +.suggestion { + cursor: pointer; + padding: 10px 20px; + display: block +} + +.suggestionHighlighted { + background-color: #ddd; +} + +.sectionContainer { + border-top: 1px dashed #ccc; + display: block; +} + +.sectionContainerFirst { + border-top: 0; + display: block; +} + +.sectionTitle { + padding: 10px 0 0 10px; + font-size: 12px; + color: #777; + display: block +} + +.suggestionName{ + font-size: 16px; + color: #596496; + flex: 1 0; +} + +.suggestionFunctie{ + font-size: 12px; + vertical-align: bottom; +} + +.suggestionWrapper{ + display: flex; + width: 100%; + height: 100%; + justify-content: flex-start; + align-items: center; +} diff --git a/components/sideNav/index.js b/components/sideNav/index.js new file mode 100644 index 0000000..77ccb42 --- /dev/null +++ b/components/sideNav/index.js @@ -0,0 +1,34 @@ +import { h, Component } from 'preact'; +import { Link } from 'preact-router/match'; +import style from './style'; +import '@material/list/dist/mdc.list.min.css'; + +import BoardLink from '../boardLink'; +import NewBoard from '../newBoard'; + +export default class Sidenav extends Component { + render({ teamName }, { addBoard }) { + return ( +
    + +
    + ); + } +} + +Sidenav.propTypes = { + addBoard: Component.PropTypes.func.isRequired +}; \ No newline at end of file diff --git a/components/sideNav/style.css b/components/sideNav/style.css new file mode 100644 index 0000000..7e0dcb9 --- /dev/null +++ b/components/sideNav/style.css @@ -0,0 +1,69 @@ +/* https://material.io/color/#!/?view.left=0&view.right=0&primary.color=F5F5F5&secondary.color=D81B60 */ +.sidebar{ + width: 25vw; + min-height: calc(100vh - 50px); + max-width: 250px; + background: #f5f5f5; + padding: 0; + /* padding-top: 1px; */ + margin: 0; + box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12); + z-index: 2; + position: fixed; + left: 0; + top: 50px +} + +.sidebar nav{ + display: flex; + flex-direction: column; +} + +.sidebar nav a{ + color: #222; + padding: 12.5px 25px; + text-decoration: none; + margin: 0; + /* flex: 3; */ +} + +.sidebar nav a:hover, +.sidebar nav a:active, +.sidebar nav a:hover + a:nth-child(2), +.sidebar nav a:active + a:nth-child(2){ + background: #320b86; + color: #fff +} + +.sidebar nav a.activeBoard { + background: #9a67ea; + color: #ffffff; +} + +.boardLinkHeader { + padding: 10px 0 5px 15px; + margin: 0; + font-weight: 700; + font-size: 1.5em; +} + +.boardLinkWrapper{ + border-bottom: 1px solid #666666; + border-top: 1px solid #666666; + display: flex; + flex-direction: column; +} + +nav .boardLinkWrapper a{ + /* padding: 10px 35px; */ + flex: 4 0 90%; + height: 100%; +} + +nav .boardLinkWrapper a:nth-child(2){ + padding: 0; + flex: 1 2 5%; + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/components/teamCard/index.js b/components/teamCard/index.js new file mode 100644 index 0000000..4f6e56d --- /dev/null +++ b/components/teamCard/index.js @@ -0,0 +1,39 @@ +import { h, Component } from 'preact'; +import ConfirmLeave from '../confirmLeave'; +import style from './style'; +import Card from 'preact-material-components/Card'; +import { Link } from 'preact-router'; +import 'preact-material-components/Card/style.css'; +import 'preact-material-components/Button/style.css'; + +export default class TeamCard extends Component { + constructor() { + super(); + + const colors = ['#F47373', '#697689', '#37D67A', '#2CCCE4', '#ff8a65', '#ba68c8', '#e91e63', '#673ab7', '#3f51b5', '#2196f3', '#009688', '#ffc107']; + this.state = { + background: colors[Math.floor(12 * Math.random())] + }; + } + render() { + return ( +
    + + +
    + {this.props.index} +
    +
    + + + + + show + + + +
    +
    + ); + } +} diff --git a/components/teamCard/style.css b/components/teamCard/style.css new file mode 100644 index 0000000..f40a75b --- /dev/null +++ b/components/teamCard/style.css @@ -0,0 +1,38 @@ +.cardWrap{ + display: inline-block; + width: 20%; + min-width: 300px; + margin: 10px 5px; +} + +.cardWrap h1{ + font-size: 25px; + text-transform: uppercase; + color: white; +} + +.cardBackcolor{ + background: #333; + color: white; + margin-left: -16px; + margin-top: -16px; + margin-right: -16px; + margin-bottom: -16px; + height: 175px; + display: flex; + justify-content: center; + align-items: center; + padding: 16px; +} + +.cardWrap button{ + width: 100%; +} + +.linkCard{ + width: 100%; + width: calc(50% - 8px); + margin-right: 8px; +} + +/* fix the button margin */ \ No newline at end of file diff --git a/components/teamInfo/index.js b/components/teamInfo/index.js new file mode 100644 index 0000000..ae19a59 --- /dev/null +++ b/components/teamInfo/index.js @@ -0,0 +1,21 @@ +import { h, Component } from 'preact'; +import BoardCard from '../boardcard'; + +import style from './style'; + +export default class TeamInfo extends Component { + render() { + return ( +
    +
    +

    {this.props.teamname}

    + { + Object + .keys(this.props.boards) + .map(key => ) + } +
    +
    + ); + } +} diff --git a/components/teamInfo/style.css b/components/teamInfo/style.css new file mode 100644 index 0000000..95016b6 --- /dev/null +++ b/components/teamInfo/style.css @@ -0,0 +1,7 @@ +.headWrap{ + display: block; +} + +.team{ + padding: 15px; +} \ No newline at end of file diff --git a/components/user/index.js b/components/user/index.js new file mode 100644 index 0000000..f22d8b9 --- /dev/null +++ b/components/user/index.js @@ -0,0 +1,59 @@ +import { h, Component } from 'preact'; +import Menu from 'preact-material-components/Menu'; +import Button from 'preact-material-components/Button'; +import 'preact-material-components/List/style.css'; +import 'preact-material-components/Menu/style.css'; +import 'preact-material-components/Button/style.css'; +import style from './style'; + +export default class UserMenu extends Component { + render() { + return ( + + + + { + this.menu = menu; + }} + open-from-top-right + class={style.boooooi} + > + + account_circle +

    profile

    +
    + + account_circle +

    edit profile

    +
    + + account_circle +

    teams

    +
    + + + account_circle +

    settings

    +
    + + account_circle +

    logout

    +
    +
    +
    +
    + ); + } +} \ No newline at end of file diff --git a/components/user/style.css b/components/user/style.css new file mode 100644 index 0000000..5acf589 --- /dev/null +++ b/components/user/style.css @@ -0,0 +1,27 @@ +nav a.boooi{ + display: inline-flex; + justify-content: center; + align-items: center; + height: 100%; + vertical-align: top; +} + +nav a.boooi div.mdc-menu-anchor div{ + top: 50px !important; +} + +nav a.booi div.booi2{ + display: flex; + justify-content: center; + align-items: center; +} + +a.boooi{ + display: flex; + justify-content: center; + align-items: center; +} + +div.boooooi{ + top: 50px !important; +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..7d52a71 --- /dev/null +++ b/index.js @@ -0,0 +1,5 @@ +import './style'; +import App from './components/app'; + +export default App; + diff --git a/routes/Teams/index.js b/routes/Teams/index.js new file mode 100644 index 0000000..1924a9e --- /dev/null +++ b/routes/Teams/index.js @@ -0,0 +1,69 @@ +import { h, Component } from 'preact'; +import style from './style'; +import base from '../../base'; +import TeamCard from '../../components/teamCard'; +import CreateTeam from '../../components/createTeam'; + +export default class Teams extends Component { + constructor() { + super(); + + this.removeTeam = this.removeTeam.bind(this); + this.addTeam = this.addTeam.bind(this); + } + + state = { + teams: {} + } + + componentWillMount(nextProps, nextState){ + this.ref = base.syncState(`users/${this.props.uuid}/teams`, { + context: this, + state: 'teams' + }); + } + + + componentWillUpdate(nextProps, nextState){ + this.ref = base.syncState(`users/${this.props.uuid}/teams`, { + context: this, + state: 'teams' + }); + } + + componentWillUnmount() { + base.removeBinding(this.ref); + } + + removeTeam = (key) => { + const teams = { ...this.state.teams }; + teams[key] = null; + this.setState({ teams }); + base.remove(`teams/${key}/members/${this.props.uuid}`); + }; + + addTeam(team) { + // update our state + const teams = { ...this.state.teams }; + // Add in our team + teams[team.name] = true; + // set state + this.setState({ teams }); + } + + render() { + return ( +
    +

    teams

    +
    + { + Object + .keys(this.state.teams) + .map(key => ) + } + +
    +
    + ); + } +} \ No newline at end of file diff --git a/routes/Teams/style.css b/routes/Teams/style.css new file mode 100644 index 0000000..7495a46 --- /dev/null +++ b/routes/Teams/style.css @@ -0,0 +1,93 @@ +.content{ + display: block; + width: 100%; +} + +.content h2{ + text-align: center; +} + +.teamsContent{ + display: flex; + justify-content: space-around; + align-items: center; + flex-wrap: wrap; +} + +.cardWrap{ + display: inline-block; + width: 20%; + min-width: 300px; + margin: 5px; +} + +.cardWrap h1{ + font-size: 25px; + text-transform: uppercase; + color: white; +} + +.cardBackcolor{ + background: #9c27b0; + color: white; + margin-left: -16px; + margin-top: -16px; + margin-right: -16px; + margin-bottom: -16px; + height: 175px; + display: flex; + justify-content: center; + align-items: center; + padding: 16px; +} + +.buttonSubmit{ + font-family: Roboto, sans-serif; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-size: 0.875rem; + font-weight: 500; + letter-spacing: 0.04em; + line-height: 2.25rem; + text-decoration: none; + text-transform: uppercase; + display: inline-block; + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; + min-width: 88px; + height: 36px; + padding: 0 16px; + border: none; + outline: none; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-appearance: none; + overflow: hidden; + vertical-align: middle; + border-radius: 2px; + color: white; +} + +.teamInput{ + height: 35px; + width: 142px; + margin-left: 0px; + margin-right: 8px; + padding: 5px 10px; + border: none; + border-bottom: 2px solid #eee; + background: none; + color: #333; + font-size: 15px; +} + +.teamInput:focus{ + border: none; + outline: none; + border-bottom: 2px solid #9c27b0; + color: #9c27b0; +} \ No newline at end of file diff --git a/routes/board/index.js b/routes/board/index.js new file mode 100644 index 0000000..456e4f7 --- /dev/null +++ b/routes/board/index.js @@ -0,0 +1,75 @@ +import { h, Component } from 'preact'; +import style from './style'; + +import BoardGroup from '../../components/groups'; +import NewGroup from '../../components/newGroup'; +import base from '../../base'; + +export default class Board extends Component { + constructor() { + super(); + this.addGroup = this.addGroup.bind(this); + this.updateGroup = this.updateGroup.bind(this); + this.removeGroup = this.removeGroup.bind(this); + this.state = { + groups: {} + }; + } + + + componentWillMount(nextProps) { + this.ref = base.syncState(`/groups/${this.props.boardName}`, { + context: this, + state: 'groups' + }); + } + + componentWillUnmount() { + base.removeBinding(this.ref); + } + + addGroup(group) { + // update our state + const groups = { ...this.state.groups }; + // add in our new group + const timestamp = Date.now(); + groups[`group_${timestamp}`] = group; + // set state + this.setState({ groups }); + } + + updateGroup(key, updatedGroup) { + const groups = { ...this.state.groups }; + groups[key] = updatedGroup; + this.setState({ groups }); + } + + removeGroup = (key) => { + const groups = { ...this.state.groups }; + groups[key] = null; + this.setState({ groups }); + }; + + render({ teamname }, { boards, members, user }) { + return ( +
    +

    {teamname}

    +
    + { + Object + .keys(this.state.groups) + .map(key => ) + } + + {/* */} +
    +
    + ); + } +} + +// , {boards, members, user} diff --git a/routes/board/style.css b/routes/board/style.css new file mode 100644 index 0000000..d5429b5 --- /dev/null +++ b/routes/board/style.css @@ -0,0 +1,15 @@ +.board{ + width: calc(100% - 250px); + height: auto; + max-height: calc(100vh - 50px); +} + +.board::-webkit-scrollbar{ + display: none; +} + +.boardGroupWrapper{ + width: 100%; + display: block; + height: auto; +} \ No newline at end of file diff --git a/routes/home/index.js b/routes/home/index.js new file mode 100644 index 0000000..e80ccdc --- /dev/null +++ b/routes/home/index.js @@ -0,0 +1,15 @@ +import { h, Component } from 'preact'; +import { Link } from 'preact-router/match'; +import style from './style'; + +export default class Home extends Component { + render() { + return ( +
    +

    Home

    +

    This is the Home component.

    + go to Webgem +
    + ); + } +} diff --git a/routes/home/style.css b/routes/home/style.css new file mode 100644 index 0000000..803eb57 --- /dev/null +++ b/routes/home/style.css @@ -0,0 +1,6 @@ +/* https://material.io/color/#!/?view.left=0&view.right=0&primary.color=F5F5F5&secondary.color=D81B60 */ +.home { + padding: 90px 20px; + min-height: 100%; + width: 100%; +} diff --git a/routes/login/index.js b/routes/login/index.js new file mode 100644 index 0000000..9606774 --- /dev/null +++ b/routes/login/index.js @@ -0,0 +1,57 @@ +import { h, Component } from 'preact'; +import style from './style'; + +import webgemLogo from '../../assets/img/logo_webgem.svg'; + +export default class Login extends Component { + renderLogin() { + return ( +
    +
    +
    +
    +
    + + + logo webgem + + +
    +
    +
    +

    Login

    + + {/*
    +
    + + +
    +
    + + +
    + +
    */} +
    + + + +
    +
    +
    +
    + ); + } + render() { + if (!this.props.uid) { + return
    {this.renderLogin()}
    ; + } + return ( +
    +
    +

    Team: boi

    +
    +
    + ); + } +} \ No newline at end of file diff --git a/routes/login/style.css b/routes/login/style.css new file mode 100644 index 0000000..a8965f2 --- /dev/null +++ b/routes/login/style.css @@ -0,0 +1,220 @@ +/* Login page */ +.login{ + display: flex; + justify-content: center; + width: 62.11%; + box-sizing: border-box; + min-width: 360px; + flex-direction: row; + align-items: center; + height: 650px; + box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); + border-radius: 15px; + background: linear-gradient(228.62deg, #FF3CAC 0%, #784BA0 51.53%, #2B86C5 100%); + overflow: hidden; +} + +/* login Wrapper */ +.loginWrap{ + display: flex; + box-sizing: border-box; + justify-content: center; + align-items: center; + height: calc(100vh - 50px); + width: 100%; + background-blend-mode: overlay; + background: linear-gradient(225deg, rgba(250, 217, 97, 0.6) 0%, rgba(255, 90, 206, 0.6) 100%); +} + +.loginWrap *{ + box-sizing: border-box +} + +.loginWith{ + display: flex; + justify-content: space-around; + flex-wrap: wrap; + align-items: stretch; + width: 100%; + margin-top: 50px; +} + +.loginWith button{ + border: none; + outline: none; + padding: 10px; + border-radius: 4px; +} + +.signup{ + width: 50%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-image: url("https://20578.tk/board/background-pics/background-login@2x.jpg"); + background-size: cover; + background-position: center; + position: relative; +} + +.loginhalf{ + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + width: 50%; + background: white; + height: 100%; +} + +.lineSub { + height: 3px; + width: 80%; + opacity: 0.8; + box-shadow: 0 0 2px 0 rgba(0,0,0,0.12); + background: linear-gradient(225deg, #FAD961 0%, #FF5ACD 100%); + display: block; + margin: 1.76% 0; +} + +.lineMain { + height: 3px; + width: 100%; + box-shadow: 0 0 2px 0 rgba(0,0,0,0.12); + background: linear-gradient(225deg, #FAD961 0%, #FF5ACD 100%); + display: block; + margin: 15px 0; +} + +.logoWrap{ + display: flex; + justify-content: space-around; + align-items: center; + flex-direction: column; + width: 55%; + z-index: 3; +} + +.logoImg{ + height: 35px; +} + +.backgroundGradient{ + background: linear-gradient(228.62deg, #FF3CAC 0%, #784BA0 51.53%, #2B86C5 100%); + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + opacity: 0.52; +} + +.loginHalf h2{ + height: 42px; + width: 104px; + color: #4A4A4A; + font-family: Roboto; + font-size: 36px; + font-weight: 900; + line-height: 42px; + text-align: center; +} + +.loginHalfH2{ + margin: 0; + color: #393939; + font-family: Roboto; + font-size: 36px; + font-weight: 900; + line-height: 37px; + text-align: center; + text-transform: uppercase; +} + +button.loginBut{ + background: linear-gradient(75deg ,#7028E4 -30%, #4776E6 120%); + border: none; + outline: none; + text-align: center; + width: 300px; + height: 50px; + border-radius: 25px; + color: white; + margin: 5px 0; + color: #FFFFFF; + font-family: Roboto; + font-size: 19px; + font-weight: 300; +} + +.lineLogin{ + width: 80%; + background: linear-gradient(225deg, #FF3CAC 0%, #784BA0 51.53%, #2B86C5 100%); + height: 4px; + margin-bottom: 10px; + margin-top: 3px; +} + +/* Form login */ +.loginForm{ + display: flex; + flex-direction: column; + align-items: center; + width: 75%; +} + +.loginForm input{ + width: 90%; + height: 40px; + border: none; + outline: none; + color: #888888; + font-family: Roboto; + font-size: 22px; + font-weight: 300; + line-height: 28px; + display: inline-block; + border-bottom: 2px solid rgba(20%,20%,20%,0.2); + transition: all .3s ease; +} + +.loginForm input:focus{ + outline: none; + border-color: rgba(20%,20%,20%,0.5); +} + +.loginForm input::placeholder{ + opacity: 0.2; + transition: all .3s ease; +} + +.loginForm input:focus::placeholder{ + opacity: 0.8; +} + +.loginForm label{ + display: inline-block; + width: 10%; +} + +.loginForm .inputWrap{ + margin: 5px 0; + width: 100%; +} + +.loginSubmit { + width: 100%; + color: #86149D; + font-family: Roboto; + font-size: 22px; + font-weight: 300; + line-height: 33px; + border: none; + outline: none; + background: none; + text-align: right; + padding: 10px; + padding-right: 20px; +} \ No newline at end of file diff --git a/routes/profile/index.js b/routes/profile/index.js new file mode 100644 index 0000000..566f7a6 --- /dev/null +++ b/routes/profile/index.js @@ -0,0 +1,47 @@ +import { h, Component } from 'preact'; +import style from './style'; + +export default class Profile extends Component { + state = { + time: Date.now(), + count: 10 + }; + + // gets called when this route is navigated to + componentDidMount() { + // start a timer for the clock: + this.timer = setInterval(this.updateTime, 1000); + } + + // gets called just before navigating away from the route + componentWillUnmount() { + clearInterval(this.timer); + } + + // update the current time + updateTime = () => { + this.setState({ time: Date.now() }); + }; + + increment = () => { + this.setState({ count: this.state.count+1 }); + }; + + // Note: `user` comes from the URL, courtesy of our router + render({ user }, { time, count }) { + return ( +
    +

    Profile: {user}

    +

    This is the user profile for a user named { user }.

    + +
    Current time: {new Date(time).toLocaleString()}
    + +

    + + {' '} + Clicked {count} times. +

    +
    + ); + } +} diff --git a/routes/profile/style.css b/routes/profile/style.css new file mode 100644 index 0000000..52453e1 --- /dev/null +++ b/routes/profile/style.css @@ -0,0 +1,6 @@ +/* https://material.io/color/#!/?view.left=0&view.right=0&primary.color=F5F5F5&secondary.color=D81B60 */ +.profile { + padding: 90px 20px; + min-height: 100%; + width: 100%; +} diff --git a/routes/team/index.js b/routes/team/index.js new file mode 100644 index 0000000..deae3b7 --- /dev/null +++ b/routes/team/index.js @@ -0,0 +1,95 @@ +import { h, Component } from 'preact'; +import style from './style'; +import Sidenav from '../../components/sideNav'; + +import base from '../../base'; +import { Router, route } from 'preact-router'; +import Board from '../board/index'; +import TeamInfo from '../../components/teamInfo/index'; +// import BoardLink from '../../components/boardLink/index'; +// import {PropTypes} from 'preact-compat'; + +export default class Team extends Component { + constructor() { + super(); + this.addBoard = this.addBoard.bind(this); + this.removeBoard = this.removeBoard.bind(this); + // this.removeFish = this.removeFish.bind(this); + // this.updateFish = this.updateFish.bind(this); + // this.loadSamples = this.loadSamples.bind(this); + // this.addToOrder = this.addToOrder.bind(this); + // this.removeFromOrder = this.removeFromOrder.bind(this); + } + + state = { + boards: {}, + members: {}, + key: '032412' + } + + componentWillMount(nextProps) { + this.ref = base.syncState(`teams/${this.props.teamName}/boards`, { + context: this, + state: 'boards' + }); + this.ref2 = base.syncState(`teams/${this.props.teamName}/members`, { + context: this, + state: 'members' + }); + } + + componentWillUnmount() { + base.removeBinding(this.ref); + base.removeBinding(this.ref2); + } + + handleRoute = e => { + this.setState({ key: Math.random() }); + this.currentUrl = e.url; + }; + + addBoard(board) { + // update our state + const boards = { ...this.state.boards }; + // add in our new fish + const timestamp = Date.now(); + boards[`board-${timestamp}`] = board; + // set state + this.setState({ boards }); + } + + removeBoard = (key) => { + const boards = { ...this.state.boards }; + base.fetch(`groups/${key}/`, { + context: this, + then(data) { + Object + .keys(data) + .map(key => base.remove(`/items/${key}`)); + base.remove(`groups/${key}`); + } + }); + boards[key] = null; + this.setState({ boards }); + route(`/team/${this.props.teamName}/`, true); + }; + + render({ teamName }, { boards, members, user }) { + return ( +
    +
    + + + + + +
    + ); + } +} +// , {boards, members, user} diff --git a/routes/team/style.css b/routes/team/style.css new file mode 100644 index 0000000..d43c6b3 --- /dev/null +++ b/routes/team/style.css @@ -0,0 +1,37 @@ +/* https://material.io/color/#!/?view.left=0&view.right=0&primary.color=F5F5F5&secondary.color=D81B60 */ +.team{ + padding: 0; + min-height: 100%; + width: 100%; +} + +.team *{ + display: inline-block; + vertical-align: top; +} + +.team * table{ + display: table; +} +.team * tr{ + display: table-row; +} +.team * td{ + display: table-cell; +} +.team * th{ + display: table-cell; +} + +.hiddenSide{ + width: 25vw; + min-height: calc(100vh - 50px); + max-width: 250px; + background: #f5f5f5; + padding: 0; + /* padding-top: 1px; */ + margin: 0; + z-index: 2; + left: 0; + top: 50px +} \ No newline at end of file diff --git a/style/index.css b/style/index.css new file mode 100644 index 0000000..4e416f5 --- /dev/null +++ b/style/index.css @@ -0,0 +1,48 @@ +/* https://material.io/color/#!/?view.left=0&view.right=0&primary.color=F5F5F5&secondary.color=D81B60 */ + +/* updated Color theme: https://material.io/color/#!/?view.left=0&view.right=0&primary.color=673AB7&secondary.color=F44336&secondary.text.color=FAFAFA */ +:root{ + --mdc-theme-primary: #9c27b0; + --theme-primary--light: #9a67ea; + --theme-primary--dark: #320b86; + --mdc-theme-secondary: #f44336; + --theme-secondary--light: #ff7961; + --theme-secondary--dark: #ba000d; + --mdc-theme-background: #fafafa; +} +html, body { + height: 100%; + width: 100%; + padding: 0; + margin: 0; + background: #FAFAFA; + font-family: 'Roboto', sans-serif; + font-weight: 300; + color: #444; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body{ + padding: 50px 0 0 0; +} + +* { + box-sizing: border-box; +} + +button{ + background-color: var(--mdc-theme-primary); +} + +.mdc-button--accent{ + background-color: var(--mdc-theme-secondary); +} + +h1,h2,h3,h4,h5,h6{ + font-weight: 400; +} + +h2{ + margin-left: 18px; +} \ No newline at end of file