#1852 worker.time-control
This commit is contained in:
parent
4685b4166b
commit
03ab88bcd9
|
@ -1,7 +1,7 @@
|
|||
@import "variables";
|
||||
|
||||
vn-chip {
|
||||
border-radius: 16px;
|
||||
border-radius: 1em;
|
||||
background-color: $color-bg;
|
||||
margin: 0 0.5em 0.5em 0;
|
||||
color: $color-font;
|
||||
|
@ -11,7 +11,7 @@ vn-chip {
|
|||
align-items: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
height: 28px;
|
||||
height: 2em;
|
||||
padding: 0 .7em;
|
||||
overflow: hidden;
|
||||
|
||||
|
@ -47,7 +47,7 @@ vn-chip {
|
|||
|
||||
vn-avatar {
|
||||
display: inline-block;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
border-radius: 50%;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('addTime', {
|
||||
Self.remoteMethodCtx('addTimeEntry', {
|
||||
description: 'Adds a new hour registry',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
|
@ -16,12 +16,12 @@ module.exports = Self => {
|
|||
root: true
|
||||
}],
|
||||
http: {
|
||||
path: `/addTime`,
|
||||
path: `/addTimeEntry`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.addTime = async(ctx, data) => {
|
||||
Self.addTimeEntry = async(ctx, data) => {
|
||||
const Worker = Self.app.models.Worker;
|
||||
const myUserId = ctx.req.accessToken.userId;
|
||||
const myWorker = await Worker.findOne({where: {userFk: myUserId}});
|
|
@ -0,0 +1,35 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('deleteTimeEntry', {
|
||||
description: 'Deletes a manual time entry for a worker if the user role is above the worker',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The time entry id',
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
returns: {
|
||||
type: 'boolean',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/deleteTimeEntry`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.deleteTimeEntry = async(ctx, id) => {
|
||||
const workerModel = Self.app.models.Worker;
|
||||
|
||||
const targetTimeEntry = await Self.findById(id);
|
||||
|
||||
const hasRightsToDelete = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk);
|
||||
|
||||
if (!hasRightsToDelete)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [
|
||||
targetTimeEntry.userFk, targetTimeEntry.timed]);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('getWorkedHours', {
|
||||
description: 'returns the total worked hours per day for a given range of dates in format YYYY-mm-dd hh:mm:ss',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The worker id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'from',
|
||||
type: 'Date',
|
||||
required: true,
|
||||
description: `The from date`
|
||||
},
|
||||
{
|
||||
arg: 'to',
|
||||
type: 'Date',
|
||||
required: true,
|
||||
description: `The to date`
|
||||
}],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/getWorkedHours`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getWorkedHours = async(id, from, to) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const stmts = [];
|
||||
|
||||
let worker = await Self.app.models.Worker.findById(id);
|
||||
let userId = worker.userFk;
|
||||
|
||||
stmts.push(`
|
||||
DROP TEMPORARY TABLE IF EXISTS
|
||||
tmp.timeControlCalculate,
|
||||
tmp.timeBusinessCalculate
|
||||
`);
|
||||
|
||||
stmts.push(new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [userId, from, to]));
|
||||
stmts.push(new ParameterizedSQL('CALL vn.timeBusiness_calculateByUser(?, ?, ?)', [userId, from, to]));
|
||||
let resultIndex = stmts.push(`
|
||||
SELECT tbc.dated, tbc.timeWorkSeconds expectedHours, tcc.timeWorkSeconds workedHours
|
||||
FROM tmp.timeBusinessCalculate tbc
|
||||
LEFT JOIN tmp.timeControlCalculate tcc ON tcc.dated = tbc.dated
|
||||
`) - 1;
|
||||
stmts.push(`
|
||||
DROP TEMPORARY TABLE IF EXISTS
|
||||
tmp.timeControlCalculate,
|
||||
tmp.timeBusinessCalculate
|
||||
`);
|
||||
|
||||
let sql = ParameterizedSQL.join(stmts, ';');
|
||||
let result = await conn.executeStmt(sql);
|
||||
|
||||
return result[resultIndex];
|
||||
};
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('timeControl', {
|
||||
description: 'Returns a range of worked hours for a given worker id',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The worker id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'dateFrom',
|
||||
type: 'datetime',
|
||||
required: true,
|
||||
description: 'the date from the time control begins in format YYYY-mm-dd-hh:mm:ss'
|
||||
},
|
||||
{
|
||||
arg: 'dateTo',
|
||||
type: 'datetime',
|
||||
required: true,
|
||||
description: 'the date when the time control finishes in format YYYY-mm-dd-hh:mm:ss'
|
||||
}],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/timeControl`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.timeControl = async(id, from, to) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const stmts = [];
|
||||
|
||||
stmts.push(new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [id, from, to]));
|
||||
|
||||
let sql = ParameterizedSQL.join(stmts, ';');
|
||||
let result = await conn.executeStmt(sql);
|
||||
|
||||
|
||||
return result[0];
|
||||
};
|
||||
};
|
|
@ -2,7 +2,8 @@ const UserError = require('vn-loopback/util/user-error');
|
|||
|
||||
module.exports = Self => {
|
||||
require('../methods/worker-time-control/filter')(Self);
|
||||
require('../methods/worker-time-control/addTime')(Self);
|
||||
require('../methods/worker-time-control/addTimeEntry')(Self);
|
||||
require('../methods/worker-time-control/deleteTimeEntry')(Self);
|
||||
|
||||
Self.rewriteDbError(function(err) {
|
||||
if (err.code === 'ER_DUP_ENTRY')
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
},
|
||||
"order": {
|
||||
"type": "Number"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
@ -26,6 +29,11 @@
|
|||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"foreignKey": "userFk"
|
||||
},
|
||||
"worker": {
|
||||
"type": "hasOne",
|
||||
"model": "Worker",
|
||||
"foreignKey": "userFk"
|
||||
},
|
||||
"warehouse": {
|
||||
"type": "belongsTo",
|
||||
|
|
|
@ -3,4 +3,5 @@ module.exports = Self => {
|
|||
require('../methods/worker/mySubordinates')(Self);
|
||||
require('../methods/worker/isSubordinate')(Self);
|
||||
require('../methods/worker/getWorkerInfo')(Self);
|
||||
require('../methods/worker/getWorkedHours')(Self);
|
||||
};
|
||||
|
|
|
@ -5,28 +5,55 @@
|
|||
data="$ctrl.hours">
|
||||
</vn-crud-model>
|
||||
<div class="main-with-right-menu">
|
||||
<vn-card class="vn-pa-lg vn-w-md">
|
||||
<vn-card class="vn-pa-lg vn-w-lg">
|
||||
<vn-table model="model" auto-load="false">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
||||
<div translate>{{::$ctrl.weekdayNames[$index].name}}</div>
|
||||
<span>{{::weekday.dated | date: 'dd'}}</span>
|
||||
<span title="{{::weekday.dated | date: 'MMMM' | translate}}" translate>
|
||||
{{::weekday.dated | date: 'MMMM'}}
|
||||
</span>
|
||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
||||
<div class="weekday" translate>{{::$ctrl.weekdayNames[$index].name}}</div>
|
||||
<div>
|
||||
<span>{{::weekday.dated | date: 'dd'}}</span>
|
||||
<span title="{{::weekday.dated | date: 'MMMM' | translate}}" translate>
|
||||
{{::weekday.dated | date: 'MMMM'}}
|
||||
</span>
|
||||
</div>
|
||||
<vn-chip
|
||||
title="{{::weekday.event.name}}"
|
||||
ng-class="{invisible: !weekday.event}">
|
||||
<vn-avatar
|
||||
ng-style="::{backgroundColor: weekday.event.color}">
|
||||
</vn-avatar>
|
||||
<div>
|
||||
{{::weekday.event.name}}
|
||||
</div>
|
||||
</vn-chip>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr>
|
||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" class="hours vn-pa-none" center>
|
||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" class="hours vn-pa-none" expand center>
|
||||
<section ng-repeat="hour in weekday.hours" center>
|
||||
<vn-icon
|
||||
icon="arrow_{{($index % 2) == 0 ? 'forward' : 'back'}}"
|
||||
title="{{(($index % 2) == 0 ? 'In' : 'Out') | translate}}">
|
||||
icon="{{
|
||||
::hour.direction == 'in' ? 'arrow_forward'
|
||||
: hour.direction == 'out' ? 'arrow_back'
|
||||
: 'arrow_forward'
|
||||
}}"
|
||||
title="{{
|
||||
::(hour.direction == 'in' ? 'In'
|
||||
: hour.direction == 'out' ? 'Out'
|
||||
: '') | translate
|
||||
}}"
|
||||
ng-class="::{'invisible': hour.direction == 'middle'}">
|
||||
</vn-icon>
|
||||
<span class="chip {{$ctrl.hourColor(hour)}}">{{hour.timed | date: 'HH:mm'}}</span>
|
||||
<vn-chip
|
||||
ng-class="::{'colored': hour.manual}"
|
||||
removable="::hour.manual"
|
||||
translate-attr="{title: 'Category'}"
|
||||
on-remove="$ctrl.showDeleteDialog(hour)">
|
||||
{{::hour.timed | date: 'HH:mm'}}
|
||||
</vn-chip>
|
||||
</section>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
|
@ -34,7 +61,7 @@
|
|||
<vn-tfoot>
|
||||
<vn-tr>
|
||||
<vn-td ng-repeat="weekday in $ctrl.weekDays" center>
|
||||
{{$ctrl.getWeekdayTotalHours(weekday)}} h.
|
||||
{{$ctrl.formatHours(weekday.workedHours)}} h.
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
<vn-tr>
|
||||
|
@ -51,6 +78,12 @@
|
|||
</vn-card>
|
||||
<vn-side-menu side="right">
|
||||
<div class="vn-pa-md">
|
||||
<div style="text-align: center;">
|
||||
<vn-label-value
|
||||
label="Finish at"
|
||||
value="{{$ctrl.getFinishTime()}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<div class="totalBox" style="text-align: center;">
|
||||
<h6 translate>Hours</h6>
|
||||
<vn-label-value
|
||||
|
@ -86,4 +119,10 @@
|
|||
<input type="button" response="CANCEL" translate-attr="{value: 'Cancel'}"/>
|
||||
<button response="ACCEPT" translate>Save</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
||||
</vn-dialog>
|
||||
<vn-confirm
|
||||
vn-id="delete-entry-dialog"
|
||||
on-response="$ctrl.deleteTimeEntry(response)"
|
||||
message="This time entry will be deleted"
|
||||
question="Are you sure you want to delete this entry?">
|
||||
</vn-confirm>
|
|
@ -2,13 +2,15 @@ import ngModule from '../module';
|
|||
import './style.scss';
|
||||
|
||||
class Controller {
|
||||
constructor($scope, $http, $stateParams, $element, vnWeekDays) {
|
||||
constructor($scope, $http, $stateParams, $element, vnWeekDays, vnApp, $translate) {
|
||||
this.$stateParams = $stateParams;
|
||||
this.$ = $scope;
|
||||
this.$http = $http;
|
||||
this.$element = $element;
|
||||
this.weekDays = [];
|
||||
this.weekdayNames = vnWeekDays.locales;
|
||||
this.vnApp = vnApp;
|
||||
this.$translate = $translate;
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
|
@ -34,9 +36,20 @@ class Controller {
|
|||
this.started = started;
|
||||
|
||||
let ended = new Date(started.getTime());
|
||||
ended.setDate(ended.getDate() + 7);
|
||||
ended.setHours(23, 59, 59, 59);
|
||||
ended.setDate(ended.getDate() + 6);
|
||||
this.ended = ended;
|
||||
|
||||
this.weekDays = [];
|
||||
let dayIndex = new Date(started.getTime());
|
||||
|
||||
while (dayIndex < ended) {
|
||||
this.weekDays.push({
|
||||
dated: new Date(dayIndex.getTime())
|
||||
});
|
||||
dayIndex.setDate(dayIndex.getDate() + 1);
|
||||
}
|
||||
|
||||
this.fetchHours();
|
||||
}
|
||||
|
||||
|
@ -49,23 +62,15 @@ class Controller {
|
|||
|
||||
set hours(value) {
|
||||
this._hours = value;
|
||||
this.weekDays = [];
|
||||
if (!this.hours) return;
|
||||
|
||||
let dayIndex = new Date(this.started.getTime());
|
||||
|
||||
while (dayIndex < this.ended) {
|
||||
let weekDay = dayIndex.getDay();
|
||||
|
||||
let hours = this.hours
|
||||
.filter(hour => new Date(hour.timed).getDay() == weekDay)
|
||||
.sort((a, b) => new Date(a.timed) - new Date(b.timed));
|
||||
|
||||
this.weekDays.push({
|
||||
dated: new Date(dayIndex.getTime()),
|
||||
hours
|
||||
});
|
||||
dayIndex.setDate(dayIndex.getDate() + 1);
|
||||
for (const weekDay of this.weekDays) {
|
||||
if (value) {
|
||||
let day = weekDay.dated.getDay();
|
||||
weekDay.hours = value
|
||||
.filter(hour => new Date(hour.timed).getDay() == day)
|
||||
.sort((a, b) => new Date(a.timed) - new Date(b.timed));
|
||||
} else
|
||||
weekDay.hours = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,61 +79,89 @@ class Controller {
|
|||
const filter = {
|
||||
where: {and: [
|
||||
{timed: {gte: this.started}},
|
||||
{timed: {lt: this.ended}}
|
||||
{timed: {lte: this.ended}}
|
||||
]}
|
||||
};
|
||||
|
||||
this.$.model.applyFilter(filter, params);
|
||||
|
||||
this.getAbsences();
|
||||
this.getWorkedHours(this.started, this.ended);
|
||||
}
|
||||
|
||||
hasEvents(day) {
|
||||
return day >= this.started && day < this.ended;
|
||||
}
|
||||
|
||||
hourColor(weekDay) {
|
||||
return weekDay.manual ? 'alert' : 'warning';
|
||||
getWorkedHours(from, to) {
|
||||
let params = {
|
||||
id: this.$stateParams.id,
|
||||
from: from,
|
||||
to: to
|
||||
};
|
||||
|
||||
const query = `api/workers/${this.$stateParams.id}/getWorkedHours`;
|
||||
return this.$http.get(query, {params}).then(res => {
|
||||
const workDays = res.data;
|
||||
const map = new Map();
|
||||
|
||||
for (const workDay of workDays) {
|
||||
workDay.dated = new Date(workDay.dated);
|
||||
map.set(workDay.dated, workDay);
|
||||
}
|
||||
|
||||
for (const weekDay of this.weekDays) {
|
||||
const workDay = workDays.find(day => {
|
||||
let from = new Date(day.dated);
|
||||
from.setHours(0, 0, 0, 0);
|
||||
|
||||
let to = new Date(day.dated);
|
||||
to.setHours(23, 59, 59, 59);
|
||||
|
||||
return weekDay.dated >= from && weekDay.dated <= to;
|
||||
});
|
||||
weekDay.expectedHours = workDay.expectedHours;
|
||||
weekDay.workedHours = workDay.workedHours;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getWeekdayTotalHours(weekday) {
|
||||
if (weekday.hours.length == 0) return 0;
|
||||
getFinishTime() {
|
||||
let weekOffset = new Date().getDay() - 1;
|
||||
if (weekOffset < 0) weekOffset = 6;
|
||||
const today = this.weekDays[weekOffset];
|
||||
|
||||
const hours = weekday.hours;
|
||||
if (today && today.workedHours) {
|
||||
const remainingTime = (today.expectedHours - today.workedHours) * 1000;
|
||||
const lastKnownTime = new Date(today.hours[today.hours.length - 1].timed).getTime();
|
||||
const finishTimeStamp = lastKnownTime + remainingTime;
|
||||
|
||||
let totalStamp = 0;
|
||||
let finishDate = new Date(finishTimeStamp);
|
||||
let hour = finishDate.getHours();
|
||||
let minute = finishDate.getMinutes();
|
||||
|
||||
hours.forEach((hour, index) => {
|
||||
let currentHour = new Date(hour.timed);
|
||||
let previousHour = new Date(hour.timed);
|
||||
if (hour < 10) hour = `0${hour}`;
|
||||
if (minute < 10) minute = `0${minute}`;
|
||||
|
||||
if (index > 0 && (index % 2 == 1))
|
||||
previousHour = new Date(hours[index - 1].timed);
|
||||
|
||||
const dif = Math.abs(previousHour - currentHour);
|
||||
|
||||
totalStamp += dif;
|
||||
});
|
||||
|
||||
if (totalStamp / 3600 / 1000 > 5)
|
||||
totalStamp += (20 * 60 * 1000);
|
||||
|
||||
weekday.total = totalStamp;
|
||||
|
||||
return this.formatHours(totalStamp);
|
||||
return `${hour}:${minute} h.`;
|
||||
}
|
||||
}
|
||||
|
||||
get weekTotalHours() {
|
||||
let total = 0;
|
||||
|
||||
this.weekDays.forEach(weekday => {
|
||||
if (weekday.total)
|
||||
total += weekday.total;
|
||||
if (weekday.workedHours)
|
||||
total += weekday.workedHours;
|
||||
});
|
||||
|
||||
return this.formatHours(total);
|
||||
}
|
||||
|
||||
formatHours(timestamp) {
|
||||
let hour = Math.floor(timestamp / 3600 / 1000);
|
||||
let min = Math.floor(timestamp / 60 / 1000 - 60 * hour);
|
||||
timestamp = timestamp || 0;
|
||||
|
||||
let hour = Math.floor(timestamp / 3600);
|
||||
let min = Math.floor(timestamp / 60 - 60 * hour);
|
||||
|
||||
if (hour < 10) hour = `0${hour}`;
|
||||
if (min < 10) min = `0${min}`;
|
||||
|
@ -159,12 +192,75 @@ class Controller {
|
|||
workerFk: this.$stateParams.id,
|
||||
timed: this.newTime
|
||||
};
|
||||
this.$http.post(`api/WorkerTimeControls/addTime`, data)
|
||||
this.$http.post(`api/WorkerTimeControls/addTimeEntry`, data)
|
||||
.then(() => this.fetchHours());
|
||||
}
|
||||
|
||||
showDeleteDialog(hour) {
|
||||
this.timeEntryToDelete = hour;
|
||||
this.$.deleteEntryDialog.show();
|
||||
}
|
||||
|
||||
deleteTimeEntry(response) {
|
||||
if (response !== 'ACCEPT') return;
|
||||
|
||||
const entryId = this.timeEntryToDelete.id;
|
||||
|
||||
this.$http.post(`api/WorkerTimeControls/${entryId}/deleteTimeEntry`).then(() => {
|
||||
this.fetchHours();
|
||||
this.vnApp.showSuccess(this.$translate.instant('Entry removed'));
|
||||
});
|
||||
}
|
||||
|
||||
getAbsences() {
|
||||
let params = {
|
||||
workerFk: this.$stateParams.id,
|
||||
started: this.started,
|
||||
ended: this.ended
|
||||
};
|
||||
|
||||
return this.$http.get(`api/WorkerCalendars/absences`, {params})
|
||||
.then(res => this.onData(res.data));
|
||||
}
|
||||
|
||||
onData(data) {
|
||||
const events = {};
|
||||
|
||||
let addEvent = (day, event) => {
|
||||
events[new Date(day).getTime()] = event;
|
||||
};
|
||||
|
||||
if (data.holidays) {
|
||||
data.holidays.forEach(holiday => {
|
||||
const holidayDetail = holiday.detail && holiday.detail.description;
|
||||
const holidayType = holiday.type && holiday.type.name;
|
||||
const holidayName = holidayDetail || holidayType;
|
||||
|
||||
addEvent(holiday.dated, {
|
||||
name: holidayName,
|
||||
color: '#ff0'
|
||||
});
|
||||
});
|
||||
}
|
||||
if (data.absences) {
|
||||
data.absences.forEach(absence => {
|
||||
const type = absence.absenceType;
|
||||
addEvent(absence.dated, {
|
||||
name: type.name,
|
||||
color: type.rgb
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.weekDays.forEach(day => {
|
||||
const timestamp = day.dated.getTime();
|
||||
if (events[timestamp])
|
||||
day.event = events[timestamp];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', '$http', '$stateParams', '$element', 'vnWeekDays'];
|
||||
Controller.$inject = ['$scope', '$http', '$stateParams', '$element', 'vnWeekDays', 'vnApp', '$translate'];
|
||||
|
||||
ngModule.component('vnWorkerTimeControl', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -4,4 +4,7 @@ Hour: Hora
|
|||
Hours: Horas
|
||||
Add time: Añadir hora
|
||||
Week total: Total semana
|
||||
Current week: Semana actual
|
||||
Current week: Semana actual
|
||||
This time entry will be deleted: Se borrará la hora fichada
|
||||
Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla?
|
||||
Finish at: Termina a las
|
|
@ -1,7 +1,7 @@
|
|||
@import "variables";
|
||||
|
||||
vn-worker-time-control {
|
||||
vn-thead > vn-tr > vn-td > div {
|
||||
vn-thead > vn-tr > vn-td > div.weekday {
|
||||
margin-bottom: 5px;
|
||||
color: $color-main
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue