1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00

Merge pull request #44 from oceanprotocol/feature/datepicker

Add date picker, ask for dateCreated
This commit is contained in:
Matthias Kretschmann 2019-04-01 13:21:07 +02:00 committed by GitHub
commit 7e340cca07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 303 additions and 104 deletions

View File

@ -1400,6 +1400,16 @@
"csstype": "^2.2.0" "csstype": "^2.2.0"
} }
}, },
"@types/react-datepicker": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-2.2.0.tgz",
"integrity": "sha512-zVAeDqkQgSdARElFXwXXpaaADyoRMo1SPsBzw6WV2iciJqS3ysSvYjqEKyTZfGbGkgw5sExEI2QKXam/KlCtyg==",
"dev": true,
"requires": {
"@types/react": "*",
"popper.js": "^1.14.1"
}
},
"@types/react-dom": { "@types/react-dom": {
"version": "16.8.3", "version": "16.8.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.8.3.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.8.3.tgz",
@ -5470,6 +5480,11 @@
"whatwg-url": "^7.0.0" "whatwg-url": "^7.0.0"
} }
}, },
"date-fns": {
"version": "2.0.0-alpha.27",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.0-alpha.27.tgz",
"integrity": "sha512-cqfVLS+346P/Mpj2RpDrBv0P4p2zZhWWvfY5fuWrXNR/K38HaAGEkeOwb47hIpQP9Jr/TIxjZ2/sNMQwdXuGMg=="
},
"date-now": { "date-now": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
@ -15893,6 +15908,18 @@
} }
} }
}, },
"react-datepicker": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-2.3.0.tgz",
"integrity": "sha512-sgauGBTUlVrne5oHuYeLhBtKy2YOuEbPo/rXRTuxP/dRJNkHpXQ+ZfRmiJDX30mh/+EGRW5ORLVGhD1YgLbEiA==",
"requires": {
"classnames": "^2.2.5",
"date-fns": "^2.0.0-alpha.23",
"prop-types": "^15.6.0",
"react-onclickoutside": "^6.7.1",
"react-popper": "^1.0.2"
}
},
"react-dev-utils": { "react-dev-utils": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-8.0.0.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-8.0.0.tgz",
@ -16113,6 +16140,11 @@
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.8.4.tgz", "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.8.4.tgz",
"integrity": "sha512-QhI19OcfhiAn60/O6bMR0w8ApXrPFCjv6+eV0I/P9/AswzjgEAx4L7VxMBCpS/jrythLa12Q9v88req+ys4YpA==" "integrity": "sha512-QhI19OcfhiAn60/O6bMR0w8ApXrPFCjv6+eV0I/P9/AswzjgEAx4L7VxMBCpS/jrythLa12Q9v88req+ys4YpA=="
}, },
"react-onclickoutside": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.8.0.tgz",
"integrity": "sha512-5Q4Rn7QLEoh7WIe66KFvYIpWJ49GeHoygP1/EtJyZjXKgrWH19Tf0Ty3lWyQzrEEDyLOwUvvmBFSE3dcDdvagA=="
},
"react-popper": { "react-popper": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz",

View File

@ -19,6 +19,7 @@
"moment": "^2.24.0", "moment": "^2.24.0",
"query-string": "^6.4.2", "query-string": "^6.4.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-datepicker": "^2.3.0",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-helmet": "^5.2.0", "react-helmet": "^5.2.0",
"react-moment": "^0.8.4", "react-moment": "^0.8.4",
@ -35,6 +36,7 @@
"@types/jest": "^24.0.11", "@types/jest": "^24.0.11",
"@types/query-string": "^6.3.0", "@types/query-string": "^6.3.0",
"@types/react": "^16.8.10", "@types/react": "^16.8.10",
"@types/react-datepicker": "^2.2.0",
"@types/react-dom": "^16.8.3", "@types/react-dom": "^16.8.3",
"@types/react-helmet": "^5.0.8", "@types/react-helmet": "^5.0.8",
"@types/react-router-dom": "^4.3.1", "@types/react-router-dom": "^4.3.1",

View File

@ -1,4 +1,5 @@
@import '../../../styles/variables'; @import '../../../styles/variables';
@import './InputDate.module.scss';
.inputWrap { .inputWrap {
background: $brand-gradient; background: $brand-gradient;
@ -10,6 +11,11 @@
&.isFocused { &.isFocused {
background: $brand-black; background: $brand-black;
} }
> div,
> div > div {
width: 100%;
}
} }
.inputWrapSearch { .inputWrapSearch {

View File

@ -1,12 +1,14 @@
import cx from 'classnames' import cx from 'classnames'
import React, { PureComponent } from 'react' import React, { PureComponent, FormEvent, ChangeEvent } from 'react'
import slugify from 'slugify' import slugify from 'slugify'
import DatePicker from 'react-datepicker'
import { ReactComponent as SearchIcon } from '../../../img/search.svg' import { ReactComponent as SearchIcon } from '../../../img/search.svg'
import Help from './Help' import Help from './Help'
import styles from './Input.module.scss'
import Label from './Label' import Label from './Label'
import Row from './Row' import Row from './Row'
import InputGroup from './InputGroup' import InputGroup from './InputGroup'
import 'react-datepicker/dist/react-datepicker-cssmodules.css'
import styles from './Input.module.scss'
interface InputProps { interface InputProps {
name: string name: string
@ -19,7 +21,13 @@ interface InputProps {
options?: string[] options?: string[]
additionalComponent?: any additionalComponent?: any
value?: string value?: string
onChange?: any onChange?(
event:
| FormEvent<HTMLInputElement>
| ChangeEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement>
): void
rows?: number rows?: number
group?: any group?: any
multiple?: boolean multiple?: boolean
@ -27,10 +35,14 @@ interface InputProps {
interface InputState { interface InputState {
isFocused: boolean isFocused: boolean
startDate?: Date
} }
export default class Input extends PureComponent<InputProps, InputState> { export default class Input extends PureComponent<InputProps, InputState> {
public state: InputState = { isFocused: false } public state: InputState = {
isFocused: false,
startDate: new Date()
}
public inputWrapClasses() { public inputWrapClasses() {
if (this.props.type === 'search') { if (this.props.type === 'search') {
@ -48,6 +60,12 @@ export default class Input extends PureComponent<InputProps, InputState> {
this.setState({ isFocused: !this.state.isFocused }) this.setState({ isFocused: !this.state.isFocused })
} }
public handleDateChange = (date: Date) => {
this.setState({
startDate: date
})
}
public InputComponent = () => { public InputComponent = () => {
const { const {
type, type,
@ -61,77 +79,91 @@ export default class Input extends PureComponent<InputProps, InputState> {
const wrapClass = this.inputWrapClasses() const wrapClass = this.inputWrapClasses()
if (type === 'select') { switch (type) {
return ( case 'select':
<div className={wrapClass}> return (
<select <div className={wrapClass}>
id={name} <select
className={styles.select} id={name}
name={name} className={styles.select}
required={required} name={name}
onFocus={this.toggleFocus} required={required}
onBlur={this.toggleFocus} onFocus={this.toggleFocus}
onChange={onChange} onBlur={this.toggleFocus}
value={value} onChange={onChange}
> value={value}
<option value="">---</option> >
<option value="">---</option>
{options &&
options
.sort((a, b) => a.localeCompare(b))
.map((option: string, index: number) => (
<option
key={index}
value={slugify(option, {
lower: true
})}
>
{option}
</option>
))}
</select>
</div>
)
case 'textarea':
return (
<div className={wrapClass}>
<textarea
id={name}
className={styles.input}
onFocus={this.toggleFocus}
onBlur={this.toggleFocus}
{...this.props}
/>
</div>
)
case 'radio':
case 'checkbox':
return (
<div className={styles.radioGroup}>
{options && {options &&
options options.map((option: string, index: number) => (
.sort((a, b) => a.localeCompare(b)) <div className={styles.radioWrap} key={index}>
.map((option: string, index: number) => ( <input
<option className={styles.radio}
key={index} id={slugify(option, {
lower: true
})}
type={type}
name={name}
value={slugify(option, { value={slugify(option, {
lower: true lower: true
})} })}
/>
<label
className={styles.radioLabel}
htmlFor={slugify(option, {
lower: true
})}
> >
{option} {option}
</option> </label>
))} </div>
</select> ))}
</div> </div>
) )
} else if (type === 'textarea') { case 'date':
return ( return (
<div className={wrapClass}> <div className={wrapClass}>
<textarea <DatePicker
id={name} selected={this.state.startDate}
className={styles.input} onChange={this.handleDateChange}
onFocus={this.toggleFocus} className={styles.input}
onBlur={this.toggleFocus} onFocus={this.toggleFocus}
{...this.props} onBlur={this.toggleFocus}
/> />
</div> </div>
) )
} else if (type === 'radio' || type === 'checkbox') {
return (
<div className={styles.radioGroup}>
{options &&
options.map((option: string, index: number) => (
<div className={styles.radioWrap} key={index}>
<input
className={styles.radio}
id={slugify(option, {
lower: true
})}
type={type}
name={name}
value={slugify(option, {
lower: true
})}
/>
<label
className={styles.radioLabel}
htmlFor={slugify(option, {
lower: true
})}
>
{option}
</label>
</div>
))}
</div>
)
} }
return ( return (

View File

@ -0,0 +1,69 @@
@import '../../../styles/variables';
//
// Date picker
//
:global .react-datepicker {
font-family: inherit;
color: inherit;
border-color: $brand-black;
}
:global .react-datepicker__header {
background: $brand-black;
border-radius: 0;
.react-datepicker__day-name,
.react-datepicker__day,
.react-datepicker__time-name,
.react-datepicker__current-month,
.react-datepicker-time__header,
.react-datepicker-year-header {
color: $brand-white;
}
}
:global .react-datepicker__current-month,
:global .react-datepicker-time__header,
:global .react-datepicker-year-header,
:global .react-datepicker__day-name {
font-weight: $font-weight-bold;
}
:global .react-datepicker__month-container {
float: none;
}
:global .react-datepicker-popper {
max-width: 16rem;
}
:global .react-datepicker-popper[data-placement^='top'] .react-datepicker__triangle:before,
:global .react-datepicker__year-read-view--down-arrow:before,
:global .react-datepicker__month-read-view--down-arrow:before,
:global .react-datepicker__month-year-read-view--down-arrow:before {
border-top-color: $brand-black;
}
:global .react-datepicker__day--selected,
:global .react-datepicker__day--in-selecting-range,
:global .react-datepicker__day--in-range,
:global .react-datepicker__month-text--selected,
:global .react-datepicker__month-text--in-selecting-range,
:global .react-datepicker__month-text--in-range {
background-color: $brand-black;
border-radius: 50%;
font-weight: $font-weight-bold;
}
:global .react-datepicker__day:hover,
:global .react-datepicker__month-text:hover {
background-color: $brand-pink;
border-radius: 50%;
font-weight: $font-weight-bold;
color: $brand-white;
}
:global .react-datepicker__day--outside-month {
color: $brand-grey-light;
}

View File

@ -26,7 +26,7 @@
"fields": { "fields": {
"description": { "description": {
"label": "Description", "label": "Description",
"description": "Add a thorough description with as much detail as possible.", "help": "Add a thorough description with as much detail as possible.",
"placeholder": "i.e. Almond sales data ", "placeholder": "i.e. Almond sales data ",
"type": "textarea", "type": "textarea",
"required": true, "required": true,
@ -34,7 +34,7 @@
}, },
"categories": { "categories": {
"label": "Categories", "label": "Categories",
"description": "Pick a category which best fits your data set.", "help": "Pick a category which best fits your data set.",
"type": "select", "type": "select",
"required": true, "required": true,
"options": [ "options": [
@ -70,6 +70,12 @@
"Communication & Journalism", "Communication & Journalism",
"Other" "Other"
] ]
},
"dateCreated": {
"label": "Creation Date",
"help": "Select the date the asset was created, or was updated for the last time.",
"type": "date",
"required": true
} }
} }
}, },

View File

@ -38,6 +38,12 @@
"type": "select", "type": "select",
"required": true, "required": true,
"options": ["Automotive", "Technology"] "options": ["Automotive", "Technology"]
},
"date": {
"label": "Date",
"placeholder": "i.e. 2019-08-12",
"type": "date",
"required": true
} }
} }
} }

View File

@ -25,6 +25,15 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
{base.copyrightHolder} {base.copyrightHolder}
</h2> </h2>
<div className={styles.metaPrimaryData}> <div className={styles.metaPrimaryData}>
<span title="Date created">
<Moment
date={base.dateCreated}
format="L"
interval={0}
/>
</span>
{base.categories ? ( {base.categories ? (
// TODO: Make this link to search for respective category // TODO: Make this link to search for respective category
<Link to={`/search?q=${base.categories[0]}`}> <Link to={`/search?q=${base.categories[0]}`}>
@ -34,14 +43,6 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
<Link to={'/search?q='}>Fake Category</Link> <Link to={'/search?q='}>Fake Category</Link>
)} )}
<span title="Date published">
<Moment
date={base.dateCreated}
format="L"
interval={0}
/>
</span>
{base.files && ( {base.files && (
<span>{base.files.length} data files</span> <span>{base.files.length} data files</span>
)} )}

View File

@ -2,7 +2,13 @@ import React from 'react'
import styles from './Item.module.scss' import styles from './Item.module.scss'
import filesize from 'filesize' import filesize from 'filesize'
const Item = ({ item, removeItem }: { item: any; removeItem: any }) => ( const Item = ({
item,
removeItem
}: {
item: { url: string; found: boolean; type: string; size: number }
removeItem(): void
}) => (
<li> <li>
<a href={item.url} className={styles.linkUrl}> <a href={item.url} className={styles.linkUrl}>
{item.url} {item.url}

View File

@ -5,7 +5,7 @@ import Button from '../../../components/atoms/Button'
import styles from './ItemForm.module.scss' import styles from './ItemForm.module.scss'
interface ItemFormProps { interface ItemFormProps {
addItem: any addItem(url: string): void
placeholder: string placeholder: string
} }

View File

@ -1,4 +1,4 @@
import React, { PureComponent } from 'react' import React, { FormEvent, PureComponent, ChangeEvent } from 'react'
import { CSSTransition, TransitionGroup } from 'react-transition-group' import { CSSTransition, TransitionGroup } from 'react-transition-group'
import Button from '../../../components/atoms/Button' import Button from '../../../components/atoms/Button'
import Help from '../../../components/atoms/Form/Help' import Help from '../../../components/atoms/Form/Help'
@ -10,9 +10,10 @@ import { serviceHost, servicePort, serviceScheme } from '../../../config'
interface File { interface File {
url: string url: string
found: boolean
checksum?: string checksum?: string
checksumType?: string checksumType?: string
contentLength?: string contentLength?: number
contentType?: string contentType?: string
resourceId?: string resourceId?: string
encoding?: string encoding?: string
@ -24,7 +25,13 @@ interface FilesProps {
placeholder: string placeholder: string
help?: string help?: string
name: string name: string
onChange: any onChange(
event:
| ChangeEvent<HTMLInputElement>
| FormEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement>
): void
} }
interface FilesStates { interface FilesStates {
@ -70,7 +77,14 @@ export default class Files extends PureComponent<FilesProps, FilesStates> {
public addItem = async (value: string) => { public addItem = async (value: string) => {
let res: any let res: any
let file: any = { url: value, found: false } let file: File = {
url: value,
found: false,
contentLength: 0,
contentType: '',
compression: ''
}
try { try {
const response = await fetch( const response = await fetch(
`${serviceScheme}://${serviceHost}:${servicePort}/api/v1/urlcheck`, `${serviceScheme}://${serviceHost}:${servicePort}/api/v1/urlcheck`,
@ -85,7 +99,7 @@ export default class Files extends PureComponent<FilesProps, FilesStates> {
res = await response.json() res = await response.json()
file.contentLength = res.result.contentLength file.contentLength = res.result.contentLength
file.contentType = res.result.contentType file.contentType = res.result.contentType
file.compression = await getFileCompression(file.contentType) file.compression = await getFileCompression(res.result.contentType)
file.found = res.result.found file.found = res.result.found
} catch (error) { } catch (error) {
// error // error

View File

@ -1,4 +1,4 @@
import React, { PureComponent } from 'react' import React, { PureComponent, FormEvent, ChangeEvent } from 'react'
import Input from '../../components/atoms/Form/Input' import Input from '../../components/atoms/Form/Input'
import Label from '../../components/atoms/Form/Label' import Label from '../../components/atoms/Form/Label'
import Row from '../../components/atoms/Form/Row' import Row from '../../components/atoms/Form/Row'
@ -8,20 +8,38 @@ import Files from './Files/'
import StepRegisterContent from './StepRegisterContent' import StepRegisterContent from './StepRegisterContent'
import styles from './Step.module.scss' import styles from './Step.module.scss'
interface Fields {
label: string
placeholder?: string
help?: string
type: string
required?: boolean
options?: string
rows?: number
}
interface StepProps { interface StepProps {
currentStep: number currentStep: number
index: number index: number
inputChange: any inputChange(
inputToArrayChange: any event:
fields?: any[] | FormEvent<HTMLInputElement>
| ChangeEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement>
): void
inputToArrayChange(
event: ChangeEvent<HTMLSelectElement> | ChangeEvent<HTMLInputElement>
): void
fields?: Fields
state: any state: any
title: string title: string
description: string description: string
next: any next(): void
prev: any prev(): void
totalSteps: number totalSteps: number
tryAgain: any tryAgain(): void
toStart: any toStart(): void
publishedDid?: string publishedDid?: string
content?: string content?: string
} }
@ -66,7 +84,6 @@ export default class Step extends PureComponent<StepProps, {}> {
description, description,
fields, fields,
inputChange, inputChange,
inputToArrayChange,
state, state,
totalSteps, totalSteps,
tryAgain, tryAgain,
@ -89,7 +106,6 @@ export default class Step extends PureComponent<StepProps, {}> {
{fields && {fields &&
Object.entries(fields).map(([key, value]) => { Object.entries(fields).map(([key, value]) => {
if (key === 'files') { if (key === 'files') {
return ( return (
<Row key={key}> <Row key={key}>

View File

@ -4,8 +4,8 @@ import Spinner from '../../components/atoms/Spinner'
import styles from './StepRegisterContent.module.scss' import styles from './StepRegisterContent.module.scss'
interface StepRegisterContentProps { interface StepRegisterContentProps {
tryAgain: any tryAgain(): void
toStart: any toStart(): void
state: any state: any
content?: string content?: string
} }

View File

@ -13,7 +13,7 @@ type AssetType = 'dataset' | 'algorithm' | 'container' | 'workflow' | 'other'
interface PublishState { interface PublishState {
name?: string name?: string
dateCreated?: Date dateCreated?: string
description?: string description?: string
files?: string[] files?: string[]
price?: number price?: number
@ -35,7 +35,7 @@ class Publish extends Component<{}, PublishState> {
public state = { public state = {
currentStep: 1, currentStep: 1,
name: '', name: '',
dateCreated: new Date(), dateCreated: '',
description: '', description: '',
files: [], files: [],
price: 0, price: 0,
@ -50,7 +50,12 @@ class Publish extends Component<{}, PublishState> {
publishingError: '', publishingError: '',
validationStatus: { validationStatus: {
1: { name: false, files: false, allFieldsValid: false }, 1: { name: false, files: false, allFieldsValid: false },
2: { description: false, categories: false, allFieldsValid: false }, 2: {
description: false,
categories: false,
dateCreated: false,
allFieldsValid: false
},
3: { 3: {
author: false, author: false,
copyrightHolder: false, copyrightHolder: false,
@ -102,7 +107,7 @@ class Publish extends Component<{}, PublishState> {
private toStart = () => { private toStart = () => {
this.setState({ this.setState({
name: '', name: '',
dateCreated: new Date(), dateCreated: '',
description: '', description: '',
files: [], files: [],
price: 0, price: 0,
@ -184,7 +189,11 @@ class Publish extends Component<{}, PublishState> {
// //
// Step 2 // Step 2
// //
if (validationStatus[2].description && validationStatus[2].categories) { if (
validationStatus[2].description &&
validationStatus[2].categories &&
validationStatus[2].dateCreated
) {
this.setState(prevState => ({ this.setState(prevState => ({
validationStatus: { validationStatus: {
...prevState.validationStatus, ...prevState.validationStatus,
@ -250,7 +259,7 @@ class Publish extends Component<{}, PublishState> {
base: Object.assign(AssetModel.base, { base: Object.assign(AssetModel.base, {
name: this.state.name, name: this.state.name,
description: this.state.description, description: this.state.description,
dateCreated: new Date().toString(), dateCreated: new Date(this.state.dateCreated).toISOString(),
author: this.state.author, author: this.state.author,
license: this.state.license, license: this.state.license,
copyrightHolder: this.state.copyrightHolder, copyrightHolder: this.state.copyrightHolder,