Date Pickers And Questions Fixed

main
Pedram Keshavarzi 3 years ago
parent 2ad4054c27
commit d7bc8ebafe
  1. 48
      package-lock.json
  2. 1
      package.json
  3. 181
      src/components/DateTimePicker/components/Calendar.js
  4. 204
      src/components/DateTimePicker/components/DatePicker.js
  5. 61
      src/components/DateTimePicker/components/Day.js
  6. 21
      src/components/DateTimePicker/components/DaysOfWeek.js
  7. 50
      src/components/DateTimePicker/components/DaysViewHeading.js
  8. 93
      src/components/DateTimePicker/components/MonthSelector.js
  9. 44
      src/components/DateTimePicker/components/MonthsViewHeading.js
  10. 3
      src/components/DateTimePicker/index.js
  11. 156
      src/components/DateTimePicker/styles/basic.css
  12. 11
      src/components/DateTimePicker/utils/assets.js
  13. 25
      src/components/DateTimePicker/utils/moment-helper.js
  14. 29
      src/components/DateTimePicker/utils/persian.js
  15. 10
      src/view/addExam/components/question.js
  16. 142
      src/view/addExam/views/AddDescription.js
  17. 18
      yarn.lock

48
package-lock.json generated

@ -25,6 +25,7 @@
"react-dropzone": "^11.4.2", "react-dropzone": "^11.4.2",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-indiana-drag-scroll": "^2.1.0", "react-indiana-drag-scroll": "^2.1.0",
"react-multi-date-picker": "^3.3.0",
"react-redux": "^7.2.6", "react-redux": "^7.2.6",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",
@ -14156,6 +14157,11 @@
"react-dom": ">=16.8.0" "react-dom": ">=16.8.0"
} }
}, },
"node_modules/react-date-object": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/react-date-object/-/react-date-object-2.1.5.tgz",
"integrity": "sha512-iAtF2nz+jZjy3w36qKx2MsxXqbiL7UgGljz9jPjLWxWCl4rDTZUMS/i7hKKiVbL1lJlmubz1Xu6W3i6CW06kTg=="
},
"node_modules/react-dev-utils": { "node_modules/react-dev-utils": {
"version": "12.0.0", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz",
@ -14320,6 +14326,15 @@
"react": ">= 16.8" "react": ">= 16.8"
} }
}, },
"node_modules/react-element-popper": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/react-element-popper/-/react-element-popper-2.1.6.tgz",
"integrity": "sha512-8va7mUmrKIkUnaM2t5Dyctd8cjgVgVcrv5vVD0FRay0sN6EPBCKa0bDi1/KmVDAjfgSIn7zQnjtc4VojcGrkgQ==",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-error-overlay": { "node_modules/react-error-overlay": {
"version": "6.0.10", "version": "6.0.10",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
@ -14391,6 +14406,19 @@
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
"peer": true "peer": true
}, },
"node_modules/react-multi-date-picker": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/react-multi-date-picker/-/react-multi-date-picker-3.3.0.tgz",
"integrity": "sha512-xAounECAkPUSnwmGCXlLevRLS9lt6IrGzD1VUJKZPbuzauwxaJLZ0mW6vnGUjui60qBY/uQeEmJftq+pEq5HgQ==",
"dependencies": {
"react-date-object": "^2.1.5",
"react-element-popper": "^2.1.6"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-redux": { "node_modules/react-redux": {
"version": "7.2.6", "version": "7.2.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
@ -27595,6 +27623,11 @@
"integrity": "sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg==", "integrity": "sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg==",
"requires": {} "requires": {}
}, },
"react-date-object": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/react-date-object/-/react-date-object-2.1.5.tgz",
"integrity": "sha512-iAtF2nz+jZjy3w36qKx2MsxXqbiL7UgGljz9jPjLWxWCl4rDTZUMS/i7hKKiVbL1lJlmubz1Xu6W3i6CW06kTg=="
},
"react-dev-utils": { "react-dev-utils": {
"version": "12.0.0", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz",
@ -27713,6 +27746,12 @@
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
}, },
"react-element-popper": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/react-element-popper/-/react-element-popper-2.1.6.tgz",
"integrity": "sha512-8va7mUmrKIkUnaM2t5Dyctd8cjgVgVcrv5vVD0FRay0sN6EPBCKa0bDi1/KmVDAjfgSIn7zQnjtc4VojcGrkgQ==",
"requires": {}
},
"react-error-overlay": { "react-error-overlay": {
"version": "6.0.10", "version": "6.0.10",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
@ -27766,6 +27805,15 @@
"integrity": "sha512-oaBdIlyu+g7PhLUvwnCsl/wvu+5tGB9I3RLjcrYLt6U1sUMzQJqplKtVxXRv9TZqRdNaAU5qNOP+dRs55QKjsA==", "integrity": "sha512-oaBdIlyu+g7PhLUvwnCsl/wvu+5tGB9I3RLjcrYLt6U1sUMzQJqplKtVxXRv9TZqRdNaAU5qNOP+dRs55QKjsA==",
"requires": {} "requires": {}
}, },
"react-multi-date-picker": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/react-multi-date-picker/-/react-multi-date-picker-3.3.0.tgz",
"integrity": "sha512-xAounECAkPUSnwmGCXlLevRLS9lt6IrGzD1VUJKZPbuzauwxaJLZ0mW6vnGUjui60qBY/uQeEmJftq+pEq5HgQ==",
"requires": {
"react-date-object": "^2.1.5",
"react-element-popper": "^2.1.6"
}
},
"react-redux": { "react-redux": {
"version": "7.2.6", "version": "7.2.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",

@ -20,6 +20,7 @@
"react-dropzone": "^11.4.2", "react-dropzone": "^11.4.2",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-indiana-drag-scroll": "^2.1.0", "react-indiana-drag-scroll": "^2.1.0",
"react-multi-date-picker": "^3.3.0",
"react-redux": "^7.2.6", "react-redux": "^7.2.6",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",

@ -0,0 +1,181 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import DaysViewHeading from './DaysViewHeading';
import DaysOfWeek from './DaysOfWeek';
import MonthSelector from './MonthSelector';
import Day from './Day';
import { getDaysOfMonth } from '../utils/moment-helper';
import moment from 'moment-jalali';
import onClickOutside from 'react-onclickoutside';
// Load Persian localisation
moment.loadPersian();
export class DateTimeCalendar extends Component {
static propTypes = {
min: PropTypes.object,
max: PropTypes.object,
styles: PropTypes.object,
selectedDay: PropTypes.object,
defaultMonth: PropTypes.object,
onSelect: PropTypes.func,
onClickOutside: PropTypes.func,
containerProps: PropTypes.object
};
static childContextTypes = {
nextMonth: PropTypes.func.isRequired,
prevMonth: PropTypes.func.isRequired,
setCalendarMode: PropTypes.func.isRequired,
setMonth: PropTypes.func.isRequired
};
static defaultProps = {
styles: require('../styles/basic.css'),
containerProps: {}
};
state = {
month: this.props.defaultMonth || this.props.selectedDay || moment(this.props.min),
selectedDay: this.props.selectedDay || null,
mode: 'days'
};
getChildContext() {
return {
nextMonth: this.nextMonth.bind(this),
prevMonth: this.prevMonth.bind(this),
setCalendarMode: this.setMode.bind(this),
setMonth: this.setMonth.bind(this)
};
}
componentWillReceiveProps({ selectedDay, defaultMonth, min }) {
if (this.props.selectedDay !== selectedDay) {
this.selectDay(selectedDay);
} else if (defaultMonth && this.props.defaultMonth !== defaultMonth && this.state.month === this.props.defaultMonth) {
this.setMonth(defaultMonth);
} else if (min && this.props.min !== min && this.state.month.isSame(this.props.min)) {
this.setMonth(min.clone());
}
}
setMode(mode) {
this.setState({ mode });
}
setMonth(month) {
this.setState({ month });
}
nextMonth() {
this.setState({
month: this.state.month.clone().add(1, 'jMonth')
});
}
prevMonth() {
this.setState({
month: this.state.month.clone().subtract(1, 'jMonth')
});
}
selectDay(selectedDay) {
const { month } = this.state;
// Because there's no `m1.isSame(m2, 'jMonth')`
if (selectedDay.format('jYYYYjMM') !== month.format('jYYYYjMM')) {
this.setState({ month: selectedDay });
}
this.setState({ selectedDay });
}
handleClickOnDay = selectedDay => {
const { onSelect } = this.props;
this.selectDay(selectedDay);
if (onSelect) {
onSelect(selectedDay);
}
};
handleClickOutside(event) {
if (this.props.onClickOutside) {
this.props.onClickOutside(event);
}
}
days = null;
lastRenderedMonth = null;
renderMonthSelector() {
const { month } = this.state;
const { styles } = this.props;
return (<MonthSelector styles={styles} selectedMonth={month}/>);
}
renderDays() {
const { month, selectedDay } = this.state;
const { children, min, max, styles, outsideClickIgnoreClass } = this.props;
let days;
if (this.lastRenderedMonth === month) {
days = this.days;
} else {
days = getDaysOfMonth(month);
this.days = days;
this.lastRenderedMonth = month;
}
return (
<div>
{children}
<DaysViewHeading styles={styles} month={month}/>
<DaysOfWeek styles={styles}/>
<div className={styles.dayPickerContainer}>
{
days.map(day => {
const isCurrentMonth = day.format('jMM') === month.format('jMM');
const disabled = (min ? day.isBefore(min) : false) || (max ? day.isAfter(max) : false);
const selected = selectedDay ? selectedDay.isSame(day, 'day') : false;
return (
<Day
key={day.format('YYYYMMDD') }
onClick={this.handleClickOnDay}
day={day}
disabled={disabled}
selected={selected}
isCurrentMonth={isCurrentMonth}
styles={styles}
/>
);
})
}
</div>
</div>
);
}
render() {
const {
selectedDay,
min,
max,
onClickOutside,
outsideClickIgnoreClass,
styles,
className
} = this.props;
const { mode } = this.state;
return (
<div className={styles.calendarContainer + ' ' + className}>
{ mode === 'monthSelector' ? this.renderMonthSelector() : this.renderDays() }
</div>
);
}
}
export default onClickOutside(Calendar);

@ -0,0 +1,204 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment-jalali';
import TetherComponent from 'react-tether';
import Calendar from './Calendar';
import classnames from 'classnames';
export const outsideClickIgnoreClass = 'ignore--click--outside';
export default class DateTimePicker extends Component {
static propTypes = {
value: PropTypes.object,
defaultValue: PropTypes.object,
onChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
children: PropTypes.node,
min: PropTypes.object,
max: PropTypes.object,
defaultMonth: PropTypes.object,
inputFormat: PropTypes.string,
removable: PropTypes.bool,
timePickerComponent: PropTypes.func,
calendarStyles: PropTypes.object,
calendarContainerProps: PropTypes.object
};
static defaultProps = {
inputFormat: 'jYYYY/jM/jD',
calendarStyles: require('../styles/basic.css'),
calendarContainerProps: {}
};
state = {
isOpen: false,
momentValue: this.props.defaultValue || null,
inputValue: this.props.defaultValue ?
this.props.defaultValue.format(this.props.inputFormat) : ''
};
setOpen(isOpen) {
this.setState({ isOpen });
}
componentWillReceiveProps(nextProps) {
if ('value' in nextProps && nextProps.value !== this.props.value) {
this.setMomentValue(nextProps.value);
}
}
setMomentValue(momentValue) {
const { inputFormat } = this.props;
if (this.props.onChange) {
this.props.onChange(momentValue);
}
let inputValue = "";
if (momentValue)
inputValue = momentValue.format(inputFormat);
this.setState({ momentValue, inputValue });
}
handleFocus() {
this.setOpen(true);
}
handleBlur(event) {
const { onBlur, inputFormat } = this.props;
const { isOpen, momentValue } = this.state;
if (isOpen) {
this.refs.input.focus();
} else if (onBlur) {
onBlur(event);
}
if (momentValue) {
const inputValue = momentValue.format(inputFormat);
this.setState({ inputValue });
}
}
handleClickOutsideCalendar() {
this.setOpen(false);
}
handleSelectDay(selectedDay) {
const { momentValue: oldValue } = this.state;
let momentValue = selectedDay.clone();
if (oldValue) {
momentValue = momentValue
.set({
hour: oldValue.hours(),
minute: oldValue.minutes(),
second: oldValue.seconds()
});
}
this.setMomentValue(momentValue);
}
handleInputChange(event) {
const { inputFormat } = this.props;
const inputValue = event.target.value;
const momentValue = moment(inputValue, inputFormat);
if (momentValue.isValid()) {
this.setState({ momentValue });
}
this.setState({ inputValue });
}
handleInputClick() {
if (!this.props.disabled) {
this.setOpen(true)
}
}
renderInput() {
let { isOpen, inputValue } = this.state;
if (this.props.value) {
let value = this.props.value;
let inputFormat = this.props.inputFormat;
inputValue = value.format(inputFormat);
}
const className = classnames(this.props.className, {
[outsideClickIgnoreClass]: isOpen
});
return (
<div>
<input
className={className}
type="text"
ref="input"
onFocus={this.handleFocus.bind(this) }
onBlur={this.handleBlur.bind(this) }
onChange={this.handleInputChange.bind(this) }
onClick={this.handleInputClick.bind(this) }
value={inputValue}
/>
</div>
);
}
renderCalendar() {
const { momentValue } = this.state;
const { timePickerComponent: TimePicker, onChange, min, max, defaultMonth, calendarStyles, calendarContainerProps } = this.props;
return (
<div>
<Calendar
min={min}
max={max}
selectedDay={momentValue}
defaultMonth={defaultMonth}
onSelect={this.handleSelectDay.bind(this) }
onClickOutside={this.handleClickOutsideCalendar.bind(this) }
outsideClickIgnoreClass={outsideClickIgnoreClass}
styles={calendarStyles}
containerProps={calendarContainerProps}
>
{
TimePicker ? (
<TimePicker
min={min}
max={max}
momentValue={momentValue}
setMomentValue={this.setMomentValue.bind(this) }
/>
) : null
}
</Calendar>
</div>
);
}
removeDate() {
const {onChange} = this.props;
if (onChange) {
onChange('');
}
this.setState({
input: '',
inputValue: ''
});
}
render() {
const { isOpen } = this.state;
return (
<TetherComponent attachment="top center">
{ this.renderInput() }
{ isOpen ? this.renderCalendar() : null }
</TetherComponent>
);
}
}

@ -0,0 +1,61 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { persianNumber } from '../utils/persian';
const styles = {
wrapper: {},
button: {
outline: 'none',
cursor: 'pointer'
}
};
export default class Day extends Component {
static propTypes = {
day: PropTypes.object.isRequired,
isCurrentMonth: PropTypes.bool,
disabled: PropTypes.bool,
selected: PropTypes.bool,
onClick: PropTypes.func
};
shouldComponentUpdate(nextProps) {
return nextProps.selected !== this.props.selected ||
nextProps.disabled !== this.props.disabled ||
nextProps.isCurrentMonth !== this.props.isCurrentMonth;
}
handleClick(event) {
event.preventDefault();
event.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
const { onClick, day } = this.props;
if (onClick) {
onClick(day);
}
}
render() {
const { day, disabled, selected, isCurrentMonth, onClick, styles, ...rest } = this.props;
const className = classnames(styles.dayWrapper, {
[styles.selected]: selected,
[styles.currentMonth]: isCurrentMonth
});
return (
<div className={className}>
<button
type="button"
onClick={this.handleClick.bind(this) }
disabled={disabled}
{...rest}
>
{ persianNumber(day.format('jD')) }
</button>
</div>
);
}
}

@ -0,0 +1,21 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// Day of week names for use in date-picker heading
const dayOfWeekNames = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'];
export default class DaysOfWeek extends Component {
static propTypes = {
styles: PropTypes.object
};
render() {
const { styles } = this.props;
return (
<div className={styles.daysOfWeek}>
{ dayOfWeekNames.map((name, key) => <div key={key}>{name}</div>) }
</div>
);
}
}

@ -0,0 +1,50 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { persianNumber } from '../utils/persian';
import { leftArrow, rightArrow } from '../utils/assets';
export default class Heading extends Component {
static propTypes = {
month: PropTypes.object.isRequired
};
static contextTypes = {
styles: PropTypes.object,
nextMonth: PropTypes.func.isRequired,
prevMonth: PropTypes.func.isRequired,
setCalendarMode: PropTypes.func.isRequired
};
handleMonthClick(event) {
const { setCalendarMode } = this.context;
event.preventDefault();
setCalendarMode('monthSelector');
}
render() {
const { nextMonth, prevMonth } = this.context;
const { month, styles } = this.props;
return (
<div className={styles.heading}>
<button className={styles.title} onClick={this.handleMonthClick.bind(this)}>
{ persianNumber(month.format('jMMMM jYYYY')) }
</button>
<button
type="button"
title="ماه قبل"
className={styles.prev}
onClick={prevMonth}
dangerouslySetInnerHTML={rightArrow}
/>
<button
type="button"
title="ماه بعد"
className={styles.next}
onClick={nextMonth}
dangerouslySetInnerHTML={leftArrow}
/>
</div>
);
}
}

@ -0,0 +1,93 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment-jalali';
import classnames from 'classnames';
import MonthsViewHeading from './MonthsViewHeading';
import { persianNumber } from '../utils/persian';
import { leftArrow, rightArrow } from '../utils/assets';
// List of months
const months = [
'فروردین',
'اردیبهشت',
'خرداد',
'تیر',
'مرداد',
'شهریور',
'مهر',
'آبان',
'آذر',
'دی',
'بهمن',
'اسفند'
];
export default class MonthSelector extends Component {
static propTypes = {
styles: PropTypes.object,
selectedMonth: PropTypes.object.isRequired
};
static contextTypes = {
setCalendarMode: PropTypes.func.isRequired,
setMonth: PropTypes.func.isRequired
};
state = {
year: this.props.selectedMonth
};
nextYear() {
this.setState({
year: this.state.year.clone().add(1, 'year')
});
}
prevYear() {
this.setState({
year: this.state.year.clone().subtract(1, 'year')
});
}
handleClick(key) {
const { setMonth, setCalendarMode } = this.context;
setMonth(moment(key, 'jM-jYYYY'));
setCalendarMode('days');
}
render() {
const { year } = this.state;
const { selectedMonth, styles } = this.props;
return (
<div className="month-selector">
<MonthsViewHeading
styles={styles}
year={year}
onNextYear={this.nextYear.bind(this) }
onPrevYear={this.prevYear.bind(this) }
/>
<div className={styles.monthsList}>
{
months.map((name, key) => {
const buttonFingerprint = (key + 1) + '-' + year.format('jYYYY');
const selectedMonthFingerprint = selectedMonth.format('jM-jYYYY');
const isCurrent = selectedMonthFingerprint === buttonFingerprint;
const className = classnames(styles.monthWrapper, {
[styles.selected]: isCurrent
});
return (
<div key={key} className={className}>
<button onClick={this.handleClick.bind(this, buttonFingerprint)}>
{name}
</button>
</div>
);
})
}
</div>
</div>);
}
}

@ -0,0 +1,44 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { persianNumber } from '../utils/persian';
import { leftArrow, rightArrow } from '../utils/assets';
export default class MonthsViewHeading extends Component {
static propTypes = {
year: PropTypes.object.isRequired,
onNextYear: PropTypes.func.isRequired,
onPrevYear: PropTypes.func.isRequired
};
static contextTypes = {
styles: PropTypes.object
};
render() {
const { year, styles } = this.props;
return (
<div className={styles.heading}>
<span className={styles.title}>
{ persianNumber(year.format('jYYYY')) }
</span>
<button
type="button"
title="سال قبل"
style={styles.navButton}
className={styles.prev}
onClick={this.props.onPrevYear}
dangerouslySetInnerHTML={rightArrow}
/>
<button
type="button"
title="سال بعد"
style={styles.navButton}
className={styles.next}
onClick={this.props.onNextYear}
dangerouslySetInnerHTML={leftArrow}
/>
</div>
);
}
}

@ -0,0 +1,3 @@
// export {DateTimeCalendar as default} from './components/Calendar';
// export {DateTimePicker as default} from './components/DatePicker';
// export { outsideClickIgnoreClass as default } from './components/DatePicker'

@ -0,0 +1,156 @@
.calendarContainer {
border-radius: 3px;
box-shadow: 0 3px 10px #dbdbdb;
border: 1px solid #cccccc;
width: 300px;
margin: auto;
text-align: center;
padding: 10px;
background-color: #fff;
}
.calendarContainer * {
box-sizing: border-box;
}
.calendarContainer .dayPickerContainer:after,
.calendarContainer .monthsList:after,
.calendarContainer .daysOfWeek:after {
content: '';
display: block;
clear: both;
}
/* Heading */
.calendarContainer .heading {
height: 42px;
font-weight: bold;
margin-bottom: 10px;
}
.calendarContainer .heading > button {
border-radius: 3px;
background: none;
margin: 5px 0;
border: 1px solid #F7F7F7;
text-align: center;
line-height: 30px;
width: 36px;
height: 32px;
cursor: pointer;
}
.calendarContainer .heading > button:hover {
background-color: #f2f2f2;
}
.calendarContainer .heading > span {
line-height: 35px;
}
.calendarContainer .heading svg {
width: 10px;
}
.calendarContainer .heading .prev {
float: right;
}
.calendarContainer .heading .next {
float: left;
}
.calendarContainer .heading .title {
line-height: 32px;
width: 120px;
height: 32px;
font-size: 1em;
margin: 5px 0;
border: 1px solid #F7F7F7;
text-align: center;
display: inline-block;
font-weight: normal;
}
/* Day wrapper styles */
.calendarContainer .dayWrapper {
padding: 5;
float: right;
width: 14.28571429%;
}
/* Day wrapper button styles */
.calendarContainer .dayWrapper button {
border: none;
background: none;
outline: none;
width: 100%;
height: 36px;
border-radius: 3px;
cursor: pointer;
}
.calendarContainer .dayWrapper button:hover {
background-color: #eeeeff;
}
.calendarContainer .dayWrapper button[disabled] {
color: #aaa;
cursor: not-allowed;
background-color: #ebebeb;
}
.calendarContainer .dayWrapper button.selected {
background-color: #337ab7;
color: #ffffff;
}
.calendarContainer .dayWrapper:not(.currentMonth) button {
opacity: .5;
}
/* Days of week row */
.calendarContainer .daysOfWeek {
border-bottom: 1px solid #EEE;
margin-bottom: 5px;
padding-bottom: 5px;
display: flex;
width: 100%;
}
.calendarContainer .daysOfWeek > div {
flex-grow: 1;
justify-content: space-between;
}
/* Month selector portion */
.calendarContainer .monthsList {
clear: both;
width: 100%;
}
.calendarContainer .monthsList button {
width: 33.33333332%;
height: 25%;
float: right;
border: 1px solid #F9F9F9;
outline: none;
font-size: 1em;
background: #fff;
padding: 10px 0;
cursor: pointer;
}
.calendarContainer .monthsList button:hover {
background: #eeeeee;
cursor: pointer;
}
/* Selected state of buttons */
.calendarContainer .selected button,
.calendarContainer .selected button:hover,
.calendarContainer .selected button:active,
.calendarContainer .selected button:focus {
background-color: #337ab7;
color: #ffffff;
}

@ -0,0 +1,11 @@
export const leftArrow = {
__html: '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 314.5 314.5" style="enable-background:new 0 0 314.5 314.5;" xml:space="preserve"><g><path class="arrow-icon" d="M125,157.5l116-116c10-10,10-24,0-34s-25-10-35,0l-133,133c-5,5-7,10-7,17s2,12,7,17l133,133c5,5,11,7,17,7s13-2,18-7c10-10,10-24,0-34L125,157.5z"/></g></svg>'
};
export const rightArrow = {
__html: '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 315.5 315.5" style="enable-background:new 0 0 315.5 315.5;" xml:space="preserve"><g><path class="arrow-icon" d="M242,141L109,8c-5-5-12-8-18-8S79,3,74,8c-10,10-10,24,0,34l116,116L74,274c-10,10-10,24,0,34s25,10,35,0l133-133c5-5,7-11,7-17C249,151,247,146,242,141z"/></g></svg>'
};
export const remove = {
__html: '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 212.982 212.982" style="enable-background:new 0 0 212.982 212.982;" xml:space="preserve"><g><path style="fill-rule:evenodd;clip-rule:evenodd;" d="M131.804,106.491l75.936-75.936c6.99-6.99,6.99-18.323,0-25.312 c-6.99-6.99-18.322-6.99-25.312,0l-75.937,75.937L30.554,5.242c-6.99-6.99-18.322-6.99-25.312,0c-6.989,6.99-6.989,18.323,0,25.312 l75.937,75.936L5.242,182.427c-6.989,6.99-6.989,18.323,0,25.312c6.99,6.99,18.322,6.99,25.312,0l75.937-75.937l75.937,75.937 c6.989,6.99,18.322,6.99,25.312,0c6.99-6.99,6.99-18.322,0-25.312L131.804,106.491z" fill="#555555"/></g></svg>'
};

@ -0,0 +1,25 @@
/**
* Get days of a month that should be shown on a month page
*
* @param month A moment object
* @returns {Array}
*/
export function getDaysOfMonth(month) {
const days = [];
const current = month.clone().startOf('jMonth');
const end = month.clone().endOf('jMonth');
// Set start to the first day of week in the last month
current.subtract((current.day() + 1) % 7, 'days');
// Set end to the last day of week in the next month
end.add(6 - (end.day() + 1) % 7, 'days');
while (current.isBefore(end)) {
days.push(current.clone());
current.add(1, 'days');
}
return days;
}

@ -0,0 +1,29 @@
const latinToPersianMap = ['۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹', '۰'];
const latinNumbers = [/1/g, /2/g, /3/g, /4/g, /5/g, /6/g, /7/g, /8/g, /9/g, /0/g];
function prepareNumber(input) {
let string;
if (typeof input === 'number') {
string = input.toString();
} else if (typeof input === 'undefined') {
string = '';
} else {
string = input;
}
return string;
}
function latinToPersian(string) {
let result = string;
for (let index = 0; index < 10; index++) {
result = result.replace(latinNumbers[index], latinToPersianMap[index]);
}
return result;
}
export function persianNumber(input) {
return latinToPersian(prepareNumber(input));
}

@ -14,9 +14,9 @@ const Question = ({data}) => {
'ز', 'ز',
] ]
return( return(
<div className='w-full flex flex-col mt-4 mb-2 justify-between items-center'> <div className='w-full flex flex-col text-blue3A mt-4 mb-2 font-bold justify-between items-center'>
<div className='w-4/5 flex flex-col bg-white rounded-xl border border-gray-300'> <div className='w-11/12 flex flex-col bg-white rounded-xl border border-gray-300'>
<div className='w-full border-b border-gray-300 p-4 text-gray-800 text-right'> <div className='w-full border-b border-gray-300 p-4 text-blue3A text-right'>
{data.title} {data.title}
</div> </div>
<div className='w-full p-3 px-4 flex justify-between items-center'> <div className='w-full p-3 px-4 flex justify-between items-center'>
@ -28,11 +28,11 @@ const Question = ({data}) => {
</div> </div>
</div> </div>
</div> </div>
<div className='w-4/5 justify-center mt-4 flex flex-wrap'> <div className='w-full justify-center mt-4 flex flex-wrap'>
{ {
data.options.map( data.options.map(
(item) => ( (item) => (
<div className='w-2/5 mx-4 my-2 flex items-center justify-center'> <div className='w-2/5 mx-2 my-2 flex items-center justify-center'>
<p className={` h-12 flex items-center justify-center w-16 rounded-lg rounded-l-none ${item.isAnswer ? 'text-white bg-green-400' : 'bg-gray-200 border border-gray-300'}`}>{gozine[item.id - 1]}</p> <p className={` h-12 flex items-center justify-center w-16 rounded-lg rounded-l-none ${item.isAnswer ? 'text-white bg-green-400' : 'bg-gray-200 border border-gray-300'}`}>{gozine[item.id - 1]}</p>
<p className={` h-12 flex items-center justify-center w-full rounded-lg rounded-r-none ${item.isAnswer ? 'bg-green-200' : 'bg-white border border-gray-300'}`}>{item.title}</p> <p className={` h-12 flex items-center justify-center w-full rounded-lg rounded-r-none ${item.isAnswer ? 'bg-green-200' : 'bg-white border border-gray-300'}`}>{item.title}</p>
</div> </div>

@ -2,82 +2,116 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import Button from "../../../components/Button"; import Button from "../../../components/Button";
import DatePicker from "../../../components/Calendar/CalendarComponents/DatePicker"; import DatePicker from "react-multi-date-picker";
import TimePicker from "react-multi-date-picker/plugins/time_picker"
import persian from "react-date-object/calendars/persian"
import persian_fa from "react-date-object/locales/persian_fa"
import InputIcon from "react-multi-date-picker/components/input_icon"
import RangeDatePicker from "../../../components/RangeDatePicker"; import RangeDatePicker from "../../../components/RangeDatePicker";
const AddExamDescription = ({grades, addExamState, dispatch, stepClick, backStep}) => { const AddExamDescription = ({ grades, addExamState, dispatch, stepClick, backStep }) => {
const [name, setName] = useState(null); const [name, setName] = useState(null);
const [type, setType] = useState(null); const [type, setType] = useState(null);
const [description, setDescription] = useState(null); const [description, setDescription] = useState(null);
const payload = {name: name, description: description, type: type}; const payload = { name: name, description: description, type: type };
const CostumeDatePickerInput = ({ openCalendar, value, handleValueChange, title }) => {
return( return(
<input
onFocus={openCalendar}
value={value}
onChange={handleValueChange}
placeholder={title}
className="w-full h-14 rounded-lg p-3 border border-gray-200 text-blue3A outline-blue-300"
/>
)
}
return (
<> <>
<div className="flex py-10 items-start px-10 border-b border-gray-300"> <div className="flex py-10 items-start px-10 border-b border-gray-300">
<div className="flex flex-col w-1/6"> <div className="flex flex-col w-1/6">
<div className="text-blue3A text-lg font-bold text-right px-4 border-r-4 border-blue65"> <div className="text-blue3A text-lg font-bold text-right px-4 border-r-4 border-blue65">
تنظیمات آزمون تنظیمات آزمون
</div>
<p className="text-sm text-right font-bold pt-4 text-gray-500">لطفا مشخضات آزمون را تکمیل کنید</p>
</div>
<div className="flex w-5/6 pl-[10%] flex-col">
<div className="flex items-center justify-between">
<input type='text' placeholder="نام آزمون" className="w-1/2 bg-white px-4 h-14 border border-gray-300 rounded-lg text-blue3A outline-blue65"
onChange={(e) => setName(e.target.value)}
/>
<RangeDatePicker title='تاریخ شروع و پایان آزمون'/> </div>
<RangeDatePicker title='تاریخ شروع و پایان آزمون'/> <p className="text-sm text-right font-bold pt-4 text-gray-500">لطفا مشخضات آزمون را تکمیل کنید</p>
</div> </div>
<textarea className="w-full mt-6 h-60 border border-gray-300 rounded-lg p-4 outline-blue65 text-blue3A" placeholder="توضیحات آزمون"
onChange={(e) => setDescription(e.target.value)}
>
</textarea> <div className="flex w-5/6 pl-[10%] flex-col">
<div className="flex items-center justify-between">
<input type='text' placeholder="نام آزمون" className="w-1/2 bg-white px-4 h-14 border border-gray-300 rounded-lg text-blue3A outline-blue65"
onChange={(e) => setName(e.target.value)}
/>
<DatePicker
format="YYYY/MM/DD HH:mm:ss"
range
calendar={persian}
locale={persian_fa}
render={<CostumeDatePickerInput title='تاریخ و زمان شروع آزمون'/>}
plugins={[
<TimePicker hideSeconds position="bottom" />,
]}
/>
<DatePicker
format="YYYY/MM/DD HH:mm:ss"
range
calendar={persian}
locale={persian_fa}
render={<CostumeDatePickerInput title='تاریخ و زمان پایان آزمون'/>}
plugins={[
<TimePicker hideSeconds position="bottom" />,
]}
/>
</div>
<textarea className="w-full mt-6 h-60 border border-gray-300 rounded-lg p-4 outline-blue65 text-blue3A" placeholder="توضیحات آزمون"
onChange={(e) => setDescription(e.target.value)}
>
</textarea>
</div>
</div>
</div> </div>
<div className="flex pt-10 items-start px-10 "> <div className="flex pt-10 items-start px-10 ">
<div className="flex flex-col w-1/6"> <div className="flex flex-col w-1/6">
<div className="text-blue3A text-lg font-bold text-right px-4 border-r-4 border-blue65"> <div className="text-blue3A text-lg font-bold text-right px-4 border-r-4 border-blue65">
تنظیمات آزمون تنظیمات آزمون
</div> </div>
<p className="text-sm text-right font-bold pt-4 text-gray-500">لطفا یک مدل از آزمون را انتخاب کنید</p> <p className="text-sm text-right font-bold pt-4 text-gray-500">لطفا یک مدل از آزمون را انتخاب کنید</p>
</div> </div>
<div className="flex w-5/6 flex-wrap">
<div className={`w-1/6 border ${type == 'bank' ? 'border-red52 text-red52 bg-pinkF5' : ' border-gray-300 text-blue3A bg-white'} font-bold cursor-pointer py-10 m-4 rounded-xl`} onClick={() => setType('bank')}>
بانک سوالات
<div className="flex w-5/6 flex-wrap">
<div className={`w-1/6 border ${type == 'bank' ? 'border-red52 text-red52 bg-pinkF5' : ' border-gray-300 text-blue3A bg-white'} font-bold cursor-pointer py-10 m-4 rounded-xl`} onClick={() => setType('bank')}>
بانک سوالات
</div> </div>
<div className={`w-1/6 border ${type == 'pdf' ? 'border-red52 text-red52 bg-pinkF5' : ' border-gray-300 text-blue3A bg-white'} font-bold cursor-pointer py-10 m-4 rounded-xl`} onClick={() => setType('pdf')}> <div className={`w-1/6 border ${type == 'pdf' ? 'border-red52 text-red52 bg-pinkF5' : ' border-gray-300 text-blue3A bg-white'} font-bold cursor-pointer py-10 m-4 rounded-xl`} onClick={() => setType('pdf')}>
ذفترچه سوالات ذفترچه سوالات
</div>
</div>
</div>
</div>
</div> </div>
<div className="flex w-full px-10 justify-end"> <div className="flex w-full px-10 justify-end">
<button className='w-1/12 mx-2 h-12 mt-4 text-red52 font-bold flex items-center justify-center' onClick={() => backStep()}> بازگشت به مرحله قبل </button> <button className='w-1/12 mx-2 h-12 mt-4 text-red52 font-bold flex items-center justify-center' onClick={() => backStep()}> بازگشت به مرحله قبل </button>
<Button <Button
title='مرحله بعدی' title='مرحله بعدی'
className='w-1/12' className='w-1/12'
onClick={() => { onClick={() => {
dispatch({type: 'ADD_EXAM_DESCRIPTION',payload }); dispatch({ type: 'ADD_EXAM_DESCRIPTION', payload });
stepClick() stepClick()
}} }}
/> />
</div> </div>

@ -7655,6 +7655,11 @@
"resolved" "https://registry.npmjs.org/react-colorful/-/react-colorful-5.5.1.tgz" "resolved" "https://registry.npmjs.org/react-colorful/-/react-colorful-5.5.1.tgz"
"version" "5.5.1" "version" "5.5.1"
"react-date-object@^2.1.5":
"integrity" "sha512-iAtF2nz+jZjy3w36qKx2MsxXqbiL7UgGljz9jPjLWxWCl4rDTZUMS/i7hKKiVbL1lJlmubz1Xu6W3i6CW06kTg=="
"resolved" "https://registry.npmjs.org/react-date-object/-/react-date-object-2.1.5.tgz"
"version" "2.1.5"
"react-dev-utils@^12.0.0": "react-dev-utils@^12.0.0":
"integrity" "sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ==" "integrity" "sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ=="
"resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz" "resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz"
@ -7714,6 +7719,11 @@
"file-selector" "^0.2.2" "file-selector" "^0.2.2"
"prop-types" "^15.7.2" "prop-types" "^15.7.2"
"react-element-popper@^2.1.6":
"integrity" "sha512-8va7mUmrKIkUnaM2t5Dyctd8cjgVgVcrv5vVD0FRay0sN6EPBCKa0bDi1/KmVDAjfgSIn7zQnjtc4VojcGrkgQ=="
"resolved" "https://registry.npmjs.org/react-element-popper/-/react-element-popper-2.1.6.tgz"
"version" "2.1.6"
"react-error-overlay@^6.0.10": "react-error-overlay@^6.0.10":
"integrity" "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==" "integrity" "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA=="
"resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz" "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz"
@ -7775,6 +7785,14 @@
"deep-equal" "1.0.1" "deep-equal" "1.0.1"
"supercluster" "^7.0.0" "supercluster" "^7.0.0"
"react-multi-date-picker@^3.3.0":
"integrity" "sha512-xAounECAkPUSnwmGCXlLevRLS9lt6IrGzD1VUJKZPbuzauwxaJLZ0mW6vnGUjui60qBY/uQeEmJftq+pEq5HgQ=="
"resolved" "https://registry.npmjs.org/react-multi-date-picker/-/react-multi-date-picker-3.3.0.tgz"
"version" "3.3.0"
dependencies:
"react-date-object" "^2.1.5"
"react-element-popper" "^2.1.6"
"react-redux@^7.2.6": "react-redux@^7.2.6":
"integrity" "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==" "integrity" "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ=="
"resolved" "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz" "resolved" "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz"

Loading…
Cancel
Save