diff --git a/backend/modules/calendar/controllers/AjaxController.php b/backend/modules/calendar/controllers/AjaxController.php new file mode 100644 index 0000000..85530c3 --- /dev/null +++ b/backend/modules/calendar/controllers/AjaxController.php @@ -0,0 +1,32 @@ +search(Yii::$app->request->queryParams)->getModels(); + $models_array = ArrayHelper::toArray($models, [ + 'backend\modules\card\models\UserCard' => [ + 'id', + 'dob', + 'fio' + ], + ]); + + $response = Yii::$app->response; + $response->format = Response::FORMAT_JSON; + $response->getHeaders()->set('Content-Type', 'application/json; charset=utf-8'); + $response->content = json_encode( + $models_array + ); + return $response; + } +}?> diff --git a/backend/modules/calendar/controllers/CalendarController.php b/backend/modules/calendar/controllers/CalendarController.php index 86ae7e3..c6760bd 100644 --- a/backend/modules/calendar/controllers/CalendarController.php +++ b/backend/modules/calendar/controllers/CalendarController.php @@ -3,7 +3,6 @@ namespace backend\modules\calendar\controllers; use backend\modules\card\models\UserCardSearch; -use common\classes\Debug; use Yii; use yii\data\ArrayDataProvider; use yii\filters\AccessControl; @@ -43,6 +42,14 @@ class CalendarController extends Controller * Renders the index view for the module * @return string */ + public function actionAlternative(){ + $searchModel = new UserCardSearch(); + $dataProvider = $searchModel->search(['month'=>date('m', strtotime('2019-07-03'))]); + return $this->render('alternative', [ + 'dataProvider' => $dataProvider + ]); + } + public function actionIndex() { $searchModel = new UserCardSearch(); diff --git a/backend/modules/calendar/views/calendar/alternative.php b/backend/modules/calendar/views/calendar/alternative.php new file mode 100644 index 0000000..4b20d21 --- /dev/null +++ b/backend/modules/calendar/views/calendar/alternative.php @@ -0,0 +1,80 @@ +title = 'Календарь ДР'; +?> + Html::a(' Назад', + Yii::$app->request->referrer, ['class' => 'btn btn-primary',]), + 'runBuild' => "function (date, content){ + this.build(date, content) + }", + 'updateContent' => "function(date){ + let monthNumber = date.substr(5, 2); + return fetch('../ajax/get-birthday-by-month?' + + 'month=' + monthNumber) + .then((res) => { + return res.json() + }) + }", + 'getColor' => "function (date, dates = null) { + for (let contentDate of dates) { + if (contentDate['dob'].substr(8, 2) == DateHelper.intToDate(date.getDate())) { + return 'success'; + } + } + }", + 'getHtmlContentForDate' => 'function (content, date) { + let flag = false + let html = ` + + + + + + + + `; + for (let i = 1; i <= content.length; i++) { + let model = content[i - 1]; + if (model["dob"].substr(8, 2) == date.substr(8, 2)) { + flag = true; + html += `` + html += `` + html += `` + html += `` + html += `` + html += `` + } + } + html += `
#ФИОДата рождения 
${i}${model["fio"]}${model["dob"]} + + + + + + +
` + if (flag) return html; + return "empty" + }' +]) ?> + + diff --git a/backend/modules/calendar/views/calendar/index.php b/backend/modules/calendar/views/calendar/index.php index 148d8a5..18a1223 100644 --- a/backend/modules/calendar/views/calendar/index.php +++ b/backend/modules/calendar/views/calendar/index.php @@ -4,9 +4,10 @@ /* @var $dataProvider yii\data\ActiveDataProvider */ use yii\grid\GridView; +use yii\helpers\Html; use yii\widgets\Pjax; ?> - +

- + 'far fa-calendar-alt']), + ['alternative'], ['class' => 'btn btn-success', 'style' => 'margin-left: 10px'])?> +
'reload']); echo GridView::widget([ diff --git a/backend/modules/card/models/UserCardSearch.php b/backend/modules/card/models/UserCardSearch.php index cb6676b..4fd7e0a 100755 --- a/backend/modules/card/models/UserCardSearch.php +++ b/backend/modules/card/models/UserCardSearch.php @@ -6,7 +6,6 @@ use common\classes\Debug; use Yii; use yii\base\Model; use yii\data\ActiveDataProvider; -use backend\modules\card\models\UserCard; /** * UserCardSearch represents the model behind the search form of `backend\modules\card\models\UserCard`. @@ -64,6 +63,10 @@ class UserCardSearch extends UserCard $query->where(['deleted_at' => null]); + if (isset($params['month'])) { + $query->andFilterWhere(['=', 'MONTH(dob)', $params['month']]); + } + // grid filtering conditions $query->andFilterWhere([ 'id' => $this->id, diff --git a/backend/web/css/calendar.css b/backend/web/css/calendar.css index e5f844d..6d9188b 100644 --- a/backend/web/css/calendar.css +++ b/backend/web/css/calendar.css @@ -291,6 +291,9 @@ text-align: center; display: flex; flex: 56px 0 0; + max-height: 44px; + box-shadow: 0px 5px 5px -5px rgba(34, 60, 80, 0.6); + align-items: center } .top-bar__days { @@ -299,7 +302,7 @@ color: #2d4338; font-weight: 100; -webkit-font-smoothing: subpixel-antialiased; - font-size: 1rem; + font-size: 18px; } .calendar__week { diff --git a/backend/widgets/AppAsset.php b/backend/widgets/AppAsset.php new file mode 100644 index 0000000..0bcc5f8 --- /dev/null +++ b/backend/widgets/AppAsset.php @@ -0,0 +1,17 @@ + { + return res.json() + }) + }"; + public $getColor = "function (date, dates = null) { + for (let contentDate of dates) { + if (contentDate['dob'].substr(8, 2) == DateHelper.intToDate(date.getDate())) { + return 'success'; + } + } + }"; + public $getHtmlContentForDate = 'function (content, date) { + console.log(content) + let flag = false + let html = ` + + + + + + + + `; + for (let i = 1; i <= content.length; i++) { + let model = content[i - 1]; + if (model["dob"].substr(8, 2) == date.substr(8, 2)) { + flag = true; + html += `` + html += `` + html += `` + html += `` + html += `` + html += `` + } + } + html += `
#ФИОДата рождения 
${i}${model["fio"]}${model["dob"]} + + + + + + +
` + if (flag) return html; + return "empty" + }'; + + /* + + CalendarHelper._updateContent = async function(){ + let monthNumber = date.substr(5, 2); + return fetch('../ajax/get-birthday-by-month?' + + 'month=' + monthNumber) + .then((res) => { + return res.json() + }) + } + + CalendarHelper._getColor = function (date, dates = null) { + for (let contentDate of dates) { + if (contentDate['dob'].substr(8, 2) == DateHelper.intToDate(date.getDate())) { + return 'success'; + } + } + } + + CalendarHelper._getHtmlContentForDate = function (content, date) { + console.log(content) + let flag = false + + let html = ` + + + + + + + `; + for (let i = 1; i <= content.length; i++) { + + let model = content[i - 1]; + if (model['dob'].substr(8, 2) == date.substr(8, 2)) { + flag = true; + html += `` + html += `` + html += `` + html += `` + html += `` + html += `` + } + } + html += `
#ФИО + Дата рождения 
${i}${model['fio']}${model['dob']} + + + + + + +
` + if (flag) return html; + return "empty" + } + */ + + public function init() + { + parent::init(); + $view = $this->getView(); + AppAsset::register($view); + } + + public function run() + { + + echo Html::beginTag('section', ['class' => 'calendar-contain']); + echo Html::beginTag('aside', ['class' => 'calendar__sidebar']); + echo Html::beginTag('section', ['class' => 'title-bar']); + echo $this->button; + echo Html::input('date', null, date('Y-m-d'), ['class' => 'form-control', 'id' => 'date',]); + echo Html::endTag('section'); + echo Html::tag('h2', date('l') . '
' . date('F d'), ['class' => 'sidebar__heading']); + echo Html::beginTag('ul', ['class' => 'sidebar__list']); + echo Html::endTag('ul'); + echo Html::endTag('aside'); + echo Html::beginTag('section', ['class' => 'calendar__days']); + echo Html::endTag('section'); + echo Html::endTag('section'); + + $this->view->registerJs(' + CalendarHelper._runBuild = ' . $this->runBuild . '; + CalendarHelper._getHtmlContentForDate = ' . $this->getHtmlContentForDate . '; + CalendarHelper._updateContent = async ' . $this->updateContent . '; + CalendarHelper._getColor = ' . $this->getColor . '; + + CalendarHelper.main()' + ); + } + +} + + +?> + + diff --git a/backend/widgets/Calendar/assets/css/style.css b/backend/widgets/Calendar/assets/css/style.css new file mode 100644 index 0000000..6d9188b --- /dev/null +++ b/backend/widgets/Calendar/assets/css/style.css @@ -0,0 +1,383 @@ +.calendar-contain { + position: relative; + left: 0; + right: 0; + border-radius: 0; + width: 100%; + overflow: hidden; + max-width: 1920px; + min-width: 450px; + margin: 1rem auto; + background-color: #f5f7f6; + box-shadow: 5px 5px 72px rgba(30, 46, 50, 0.5); + color: #040605; +} + +@media screen and (min-width: 55em) { + .calendar-contain { + margin: auto; + top: 5%; + } +} + +.title-bar { + position: relative; + width: 100%; + display: flex; + text-align: right; + background: #f5f7f6; + padding: 0.5rem; + margin-bottom: 0; +} + +.title-bar:after { + display: table; + clear: both; +} + +.title-bar__burger { + display: block; + position: relative; + float: left; + overflow: hidden; + margin: 0; + padding: 0; + width: 38px; + height: 30px; + font-size: 0; + text-indent: -9999px; + appearance: none; + box-shadow: none; + border: none; + cursor: pointer; + background: none; +} + +.title-bar__burger:focus { + outline: none; +} + +.burger__lines { + display: block; + position: absolute; + width: 18px; + top: 15px; + left: 0; + right: 0; + margin: auto; + height: 1px; + background: #040605; +} + +.burger__lines:before, .burger__lines:after { + position: absolute; + display: block; + left: 0; + width: 100%; + height: 1px; + background-color: #040605; + content: ""; +} + +.burger__lines:before { + top: -5px; +} + +.burger__lines:after { + bottom: -5px; +} + +.title-bar__year { + display: block; + position: relative; + float: left; + font-size: 1rem; + line-height: 30px; + width: 43%; + padding: 0 0.5rem; + text-align: left; +} + +@media screen and (min-width: 55em) { + .title-bar__year { + width: 27%; + } +} + +.title-bar__month { + position: relative; + /*float: center;*/ + font-size: 1rem; + line-height: 30px; + width: 22%; + padding: 0 0.5rem; + text-align: center; + margin-right: 67px; + word-spacing: 30px; +} + +@media screen and (min-width: 55em) { + .title-bar__month { + width: 12%; + } +} + +.title-bar__minimize, .title-bar__maximize, .title-bar__close { + position: relative; + float: left; + width: 34px; + height: 34px; +} + +.title-bar__minimize:before, .title-bar__maximize:before, .title-bar__close:before, .title-bar__minimize:after, .title-bar__maximize:after, .title-bar__close:after { + top: 30%; + right: 30%; + bottom: 30%; + left: 30%; + content: " "; + position: absolute; + border-color: #8e8e8e; + border-style: solid; + border-width: 0 0 2px 0; +} + +.title-bar .title-bar__controls { + display: inline-block; + vertical-align: top; + position: relative; + float: right; + width: auto; +} + +.title-bar .title-bar__controls:before, .title-bar .title-bar__controls:after { + content: none; +} + +.title-bar .title-bar__minimize:before { + border-bottom-width: 2px; +} + +.title-bar .title-bar__maximize:before { + border-width: 1px 1px 2px 1px; +} + +.title-bar .title-bar__close:before, .title-bar .title-bar__close:after { + bottom: 50%; + top: 49.9%; +} + +.title-bar .title-bar__close:before { + transform: rotate(45deg); +} + +.title-bar .title-bar__close:after { + transform: rotate(-45deg); +} + +.calendar__sidebar { + width: 100%; + margin: 0 auto; + float: none; + background: linear-gradient(120deg, #eff3f3, #e1e7e8); + padding-bottom: 0.7rem; +} + +@media screen and (min-width: 55em) { + .calendar__sidebar { + position: absolute; + height: 100%; + width: 50%; + float: left; + margin-bottom: 0; + } +} + +.calendar__sidebar .content { + padding: 2rem 1.5rem 2rem 4rem; + color: #040605; +} + +.sidebar__nav { + display: flex; + align-items: center; + justify-content: flex-start; + margin-bottom: 0.9rem; + padding: 0.7rem 1rem; + background-color: #f5f7f6; +} + +.sidebar__nav-item { + display: inline-block; + width: 22px; + margin-right: 23px; + padding: 0; + opacity: 0.8; +} + +.sidebar__list { + list-style: none; + margin: 0; + padding-left: 1rem; + padding-right: 1rem; +} + +.sidebar__list-item { + margin: 1.2rem 0; + color: #2d4338; + font-weight: 100; + font-size: 1rem; +} + +.list-item__time { + display: inline-block; + width: 60px; +} + +@media screen and (min-width: 55em) { + .list-item__time { + margin-right: 2rem; + } +} + +.sidebar__list-item--complete { + color: rgba(4, 6, 5, 0.3); +} + +.sidebar__list-item--complete .list-item__time { + color: rgba(4, 6, 5, 0.3); +} + +.sidebar__heading { + font-size: 2.2rem; + font-weight: bold; + padding-left: 1rem; + padding-right: 1rem; + margin-bottom: 3rem; + margin-top: 1rem; +} + +.sidebar__heading span { + float: right; + font-weight: 300; +} + +.calendar__heading-highlight { + color: #2d444a; + font-weight: 900; +} + +.calendar__days { + display: flex; + flex-flow: column wrap; + align-items: stretch; + width: 100%; + float: none; + min-height: 580px; + height: 100%; + font-size: 12px; + /*padding: 0.8rem 0 1rem 1rem;*/ + background: #f5f7f6; +} + +@media screen and (min-width: 55em) { + .calendar__days { + width: 50%; + float: right; + } +} + +.calendar__top-bar { + background: #b8cad6; + text-align: center; + display: flex; + flex: 56px 0 0; + max-height: 44px; + box-shadow: 0px 5px 5px -5px rgba(34, 60, 80, 0.6); + align-items: center +} + +.top-bar__days { + width: 100%; + padding: 0 5px; + color: #2d4338; + font-weight: 100; + -webkit-font-smoothing: subpixel-antialiased; + font-size: 18px; +} + +.calendar__week { + display: flex; + flex: 1 1 0; +} + +.calendar__day { + text-align: center; + display: flex; + flex-flow: column wrap; + + width: 100%; + justify-content: space-around; + /*padding: 1.9rem 0.2rem 0.2rem;*/ +} + + +.calendar__date { + /*margin: 25px;*/ + color: #040605; + font-size: 1.7rem; + font-weight: 600; + line-height: 0.7; +} + +@media screen and (min-width: 55em) { + .calendar__date { + font-size: 2.3rem; + } +} + +.calendar__week .inactive .calendar__date, .calendar__week .inactive .task-count { + color: #c6c6c6; +} + +.calendar__week .today .calendar__date { + color: #fd588a; +} + +.calendar__task { + color: #040605; + display: flex; + font-size: 0.8rem; +} + +@media screen and (min-width: 55em) { + .calendar__task { + font-size: 1rem; + } +} + +.calendar__task.calendar__task--today { + color: #fd588a; +} + + +.danger { + color: red; +} + +.success { + color: green; +} + +.calendar__day:hover { + color: #0a0a0a; + background: #c2c2c2; + cursor: pointer; +} +.calendar__day:active { + background: #727272; + cursor: pointer; +} + +.active_day { + background: #cdcfce; +} \ No newline at end of file diff --git a/backend/widgets/Calendar/assets/js/script.js b/backend/widgets/Calendar/assets/js/script.js new file mode 100644 index 0000000..e69de29 diff --git a/backend/widgets/Calendar/assets/js/src/CalendarHelper.js b/backend/widgets/Calendar/assets/js/src/CalendarHelper.js new file mode 100644 index 0000000..aa8dbc7 --- /dev/null +++ b/backend/widgets/Calendar/assets/js/src/CalendarHelper.js @@ -0,0 +1,186 @@ +class CalendarHelper { + + static build(date, dates = null) { + const _daysNames = `
+ Mon + Tue + Wed + Thu + Fri + Sat + Sun +
`; + + + let month = DateHelper.getMonth(date) + + let html = `
`, index = 1, indexRaw = 0; + + for (let i = 0, dayNum = month[i]; i < month.length; i++, dayNum = month[i]) { + let className = `` + if (this._isInactive(dayNum, i)) { + className = 'inactive' + } + let color = this._getColor(new Date(date.getFullYear(), date.getMonth(), dayNum), dates) + html += this._getCalendarDay(dayNum, className, color) + if ((i + 1) % 7 == 0) { + + html += `
` + + + } + + } + html = removeExcessTagSection(html) + + document.querySelector('.calendar__days').innerHTML = _daysNames + html + + + function removeExcessTagSection(html) { + return html.substr(0, html.length - 9) + } + } + + static main(day = null) { + let datePicker = document.querySelector('#date'); + let oldDate = datePicker.value.substr(0, 7); + + let nameDateBoard = document.querySelector('.sidebar__heading'); + let contentBoard = document.querySelector('.sidebar__list'); + + this._updateContent(datePicker.value) + .then(content => { + + this._runBuild(DateHelper.stringToDate(datePicker.value), content) + + datePicker.onchange = function (day = null) { + let days = document.querySelectorAll('.calendar__day ') + + for (let i = 0; i < days.length; i++) { + if (days[i].classList.contains('active_day')) + days[i].classList.remove('active_day') + } + + if (!CalendarHelper.isOldDatePicker(datePicker, oldDate)) { + oldDate = datePicker.value.substr(0, 7); + + CalendarHelper._updateContent(datePicker.value) + .then(content => { + + CalendarHelper.main(day) + let date = new Date(datePicker.value); + let monthName = date.toLocaleString('default', {month: 'long'}); + let dayWeekName = date.toLocaleString('default', {weekday: 'long'}); + nameDateBoard.innerHTML = `${dayWeekName}
${monthName} ${datePicker.value.substr(8, 2)}`; + contentBoard.innerHTML = CalendarHelper._getHtmlContentForDate(content, datePicker.value) + }) + + } + let date = new Date(datePicker.value); + let monthName = date.toLocaleString('default', {month: 'long'}); + let dayWeekName = date.toLocaleString('default', {weekday: 'long'}); + nameDateBoard.innerHTML = `${dayWeekName}
${monthName} ${datePicker.value.substr(8, 2)}`; + contentBoard.innerHTML = CalendarHelper._getHtmlContentForDate(content, datePicker.value) + } + + let days = document.querySelectorAll('.calendar__day'); + + for (let i = 0; i < Object.keys(days).length; i++) { + let dateDay = parseInt(days[i].textContent); + if (day) { + if (parseInt(day.textContent) == dateDay && !days[i].classList.contains('inactive')) { + days[i].classList.add('active_day') + } + } + + if (days[i].classList.contains('inactive')) { + days[i].onclick = function () { + let date = CalendarHelper._getFutureDate(datePicker.value, parseInt(days[i].textContent)) + datePicker.value = date; + datePicker.onchange(this) + } + } else { + days[i].onclick = function () { + datePicker.value = datePicker.value.substr(0, 8) + DateHelper.intToDate(dateDay); + datePicker.onchange(this) + days[i].classList.add('active_day') + } + } + } + + + }) + } + + static _getHtmlContentForDate(content, date) { + /* making html content; Example: return "In this day Birthday of " +content['date']['user']; */ + } + + + static _runBuild(date, content) { + /*making something for dates from content = [date1, date2, ...]*/ + + this.build(date, content) + } + + static + async _updateContent(date) { + let monthNumber = date.substr(5, 2); + return fetch('../ajax/get-birthday-by-month?' + + 'month=' + monthNumber) + .then((res) => { + return res.json() + }) + + } + + static isOldDatePicker(datePicker, oldDate) { + if (datePicker.value.substr(0, 7) == oldDate) + return true; + return false + } + + static _isInactive(numDay, i) { + if (i < 8 && numDay !== i + 1) { + return (numDay > i) + } else { + return (i - 8) > numDay + } + } + + static _getCalendarDay(dayNum, className = ``, color = ``) { + return `
+ ${dayNum} +
`; + } + + static _getColor(date, dates = null) { + if (dates != null && dates.includes(DateHelper.dateToString(date))) { + return 'success'; + } + if ([6, 0].includes(date.getDay())) + return; + + return 'danger'; + } + + static _getFutureDate(dat, value) { + let date = new Date(dat); + if (value < 8) { + if (date.getMonth() == 11) { + date = new Date(date.getFullYear() + 1, 0, value); + } else { + date = new Date(date.getFullYear(), date.getMonth() + 1, value); + } + } else { + if (date.getMonth() == 0) { + date = new Date(date.getFullYear() - 1, 11, value); + } else { + date = new Date(date.getFullYear(), date.getMonth() - 1, value); + } + } + return date.getFullYear() + '-' + DateHelper.intToDate(date.getMonth() + 1) + '-' + DateHelper.intToDate(value); + + } + +} diff --git a/backend/widgets/Calendar/assets/js/src/DateHelper.js b/backend/widgets/Calendar/assets/js/src/DateHelper.js new file mode 100644 index 0000000..c2a524a --- /dev/null +++ b/backend/widgets/Calendar/assets/js/src/DateHelper.js @@ -0,0 +1,87 @@ +class DateHelper { + static getMonth(date) { + date = this.nextMonth(date) + date = this.prevDay(date) + let days = [], last = date, quantity = this.getQuantity(date) + for (let i = 0; i < (7 - last.getDay()) % 7; i++) { + days.push((7 - last.getDay()) % 7 - i) + } + for (let i = 0; i < last.getDate(); i++) { + days.push(last.getDate() - i) + } + date = this.prevMonth(date) + date = this.nextMonth(date) + date = this.prevDay(date) + for (let i = 0, lenDays = quantity - days.length; i < lenDays; i++) { + days.push(date.getDate() - i) + } + return days.reverse(); + } + + static getQuantity(date) { + if (this.isDayOff(date)) { + return 42 + } + if (this.prevDay(this.nextMonth(date)).getDate() == 28) { + return 28; + } + return 35 + + } + + static isDayOff(date) { + return [6, 0].includes(new Date(date.getFullYear(), date.getMonth(), 1).getDay()) + } + + static prevDay(date) { + return new Date(date.getTime() - 1000 * 3600 * 24) + } + + static nextMonth(date) { + if (this.isDecember(date)) { + return new Date(date.getFullYear() + 1, 0, 1) + } + return new Date(date.getFullYear(), date.getMonth() + 1, 1) + } + + static prevMonth(date) { + if (this.isJanuary(date)) { + return new Date(date.getFullYear() - 1, 11, 1) + } + return new Date(date.getFullYear(), date.getMonth() - 1, 1) + } + + static isDecember(date) { + return 11 == date.getMonth() + } + + static isJanuary(date) { + return 0 == date.getMonth() + } + + static intToDate(number_date) { + if (Math.floor(number_date / 10) === 0) + number_date = '0' + number_date; + return number_date + } + + static dateToString(date) { + let year = date.getFullYear() + let day = this.intToDate(date.getDate()) + let month = this.intToDate(this.isDecember(date) ? 0 : date.getMonth() + 1) + return year + '-' + month + '-' + day + } + + static stringToDate(date) { + let month = parseInt(date.substr(5, 2)) + return new Date(date.substr(0, 4), month, date.substr(7, 2)) + } + + static getDates(array) { + let dates = [] + for (let model of array) { + dates.push(model['date']) + } + return dates + } +}