mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
feat(timeSheetExport): half way there
This commit is contained in:
parent
c7ad410aa4
commit
bfc79e8cb9
8 changed files with 547 additions and 200 deletions
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -1123,6 +1123,15 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.4.2.tgz",
|
||||
"integrity": "sha512-cDB930/7MbzaGF6U3IwSQp6XBru8xWajF5PV2YZZeV8DyiliTuld11afVztGI9+yJZ29il5E+NpGA6ooV/Cjkg=="
|
||||
},
|
||||
"@types/moment-duration-format": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/moment-duration-format/-/moment-duration-format-2.2.2.tgz",
|
||||
"integrity": "sha512-CuYswsMI3y5uR5sD9i/VUqIbZrsYN2eaCs7nH3qpDl2CZlNI48mjMf4ve2RpQ/65irprtnQVetfnea9my+jqcg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"moment": ">=2.14.0"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "8.9.5",
|
||||
"resolved": "http://registry.npmjs.org/@types/node/-/node-8.9.5.tgz",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@
|
|||
"@ngrx/schematics": "^6.1.0",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/moment-duration-format": "^2.2.2",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.2.1",
|
||||
"dbus-native": "^0.2.5",
|
||||
|
|
|
|||
|
|
@ -1,196 +1,207 @@
|
|||
<h1 mat-dialog-title="">Time Sheet Export</h1>
|
||||
<div class="mat-typography">
|
||||
<h1 mat-dialog-title="">Time Sheet Export</h1>
|
||||
|
||||
<form (submit)="save()">
|
||||
<div mat-dialog-content=""
|
||||
class="dialog-content">
|
||||
<collapsible class="help-collapsible"
|
||||
title="What is this and how does it work?"
|
||||
[initiallyExpanded]="opts.spreadsheetId">
|
||||
<div class="info">
|
||||
<p>This view allows you to export your worked time to a google sheet. You need to allow for your Google Spreadsheets to be accessed by Super Productivity. You also need to create a spreadsheet with a headings in the first row nad specify it's ID in the input field (<a external-link
|
||||
target="_blank"
|
||||
href="https://stackoverflow.com/questions/36061433/how-to-do-i-locate-a-google-spreadsheet-id">Ho to find the id of a spreadsheet?</a>).
|
||||
</p>
|
||||
<p>After successfully loading your spreadsheet a table will show up with 4 rows. The first row shows the heading you specified in the spreadsheet itself.</p>
|
||||
<p>The second row is for informational purposes and shows the last row from the spreadsheet.</p>
|
||||
<p>The forth row is a list of values you can directly enter save to the spreadsheet.</p>
|
||||
<p>The third row is there to automatically define some values for the forth row. There are several special strings you can enter into the cells:</p>
|
||||
</div>
|
||||
|
||||
<dl class="possible-properties">
|
||||
<dt>{{ '{' }}startTime}</dt>
|
||||
<dd>The time when you first used this app today. It's possible to round this via the options.</dd>
|
||||
|
||||
<dt>{{ '{' }}currentTime}</dt>
|
||||
<dd>The current time. Could be used for the for the end time of todays working day It's possible to round this via the options.</dd>
|
||||
|
||||
<dt>{{ '{' }}date}</dt>
|
||||
<dd>Todays date in standard format (mm/dd/yyyy)</dd>
|
||||
|
||||
<dt>{{ '{' }}date:DD/MM/YYYY} (example)</dt>
|
||||
<dd>Date with a custom date format string.</dd>
|
||||
|
||||
<dt>{{ '{' }}taskTitles}</dt>
|
||||
<dd>Comma separated (parent) task titles</dd>
|
||||
|
||||
<dt>{{ '{' }}subTaskTitles}</dt>
|
||||
<dd>Comma separated sub task titles</dd>
|
||||
|
||||
<dt>{{ '{' }}totalTime}</dt>
|
||||
<dd>The total time you spend working on your todays tasks.</dd>
|
||||
</dl>
|
||||
|
||||
<p>In addition to this there are several options you can use to modify the calculation of those values.</p>
|
||||
</collapsible>
|
||||
|
||||
<collapsible title="Options"
|
||||
class="options-collapsible"
|
||||
[initiallyExpanded]="opts.spreadsheetId">
|
||||
<mat-slide-toggle [(ngModel)]="opts.isAutoLogin">Auto-login and load data next time</mat-slide-toggle>
|
||||
<!--<mat-slide-toggle [(ngModel)]="opts.isAutoFocusEmpty">Auto-focus first empty field after loading table headings</mat-slide-toggle>-->
|
||||
<mat-slide-toggle [(ngModel)]="opts.isRoundWorkTimeUp"
|
||||
(change)="updateDefaults()">Always round work time up
|
||||
</mat-slide-toggle>
|
||||
<mat-form-field>
|
||||
<label>Round start time to</label>
|
||||
<mat-select (change)="updateDefaults()"
|
||||
[(ngModel)]="opts.roundStartTimeTo">
|
||||
<mat-option><em>don't round</em></mat-option>
|
||||
<mat-option *ngFor="let roundOption of roundTimeOptions"
|
||||
[value]="roundOption.id">
|
||||
{{roundOption.title}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<label>Round end time to</label>
|
||||
<mat-select (change)="updateDefaults()"
|
||||
[(ngModel)]="opts.roundEndTimeTo">
|
||||
<mat-option><em>don't round</em></mat-option>
|
||||
<mat-option *ngFor="let roundOption of roundTimeOptions"
|
||||
[value]="roundOption.id">
|
||||
{{roundOption.title}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<label>Round time worked to</label>
|
||||
<mat-select (change)="updateDefaults()"
|
||||
[(ngModel)]="opts.roundWorkTimeTo">
|
||||
<mat-option><em>don't round</em></mat-option>
|
||||
<mat-option *ngFor="let roundOption of roundTimeOptions"
|
||||
[value]="roundOption.id">
|
||||
{{roundOption.title}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</collapsible>
|
||||
|
||||
<div>
|
||||
<button mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn
|
||||
*ngIf="!GoogleApi.isLoggedIn"
|
||||
(click)="login()">Login
|
||||
</button>
|
||||
<button mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn
|
||||
*ngIf="GoogleApi.isLoggedIn"
|
||||
(click)="logout()">Logout
|
||||
</button>
|
||||
<button mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn
|
||||
[disabled]="!opts.spreadsheetId"
|
||||
*ngIf="GoogleApi.isLoggedIn"
|
||||
(click)="readSpreadsheet()">Read spreadsheet
|
||||
</button>
|
||||
<a mat-raised-button=""
|
||||
color="primary"
|
||||
*ngIf="GoogleApi.isLoggedIn"
|
||||
external-link
|
||||
target="_blank"
|
||||
href="https://myaccount.google.com/permissions">Revoke permissions
|
||||
</a>
|
||||
<a mat-raised-button=""
|
||||
color="primary"
|
||||
external-link
|
||||
target="_blank"
|
||||
*ngIf="GoogleApi.isLoggedIn && opts.spreadsheetId"
|
||||
href="https://docs.google.com/spreadsheets/d/{{opts.spreadsheetId}}">Edit spreadsheet
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="loading-spinner"
|
||||
*ngIf="isLoading && !GoogleApi.isLoggedIn">
|
||||
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
|
||||
</div>
|
||||
|
||||
<section *ngIf="GoogleApi.isLoggedIn">
|
||||
<mat-form-field class="md-block">
|
||||
<label>Spreadsheet ID</label>
|
||||
<input type="text"
|
||||
[(ngModel)]="opts.spreadsheetId">
|
||||
</mat-form-field>
|
||||
|
||||
<div class="loading-spinner"
|
||||
*ngIf="isLoading && GoogleApi.isLoggedIn">
|
||||
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
|
||||
</div>
|
||||
|
||||
<table class="export-input-table"
|
||||
*ngIf="headings">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="heading"
|
||||
*ngFor="let heading of headings"
|
||||
[innerHtml]="heading"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Last saved row</th>
|
||||
<td *ngFor="let col of lastRow">{{col}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default</th>
|
||||
<td class="default"
|
||||
*ngFor="let heading of headings; let i = index;">
|
||||
<input type="text"
|
||||
(change)="updateDefaults()"
|
||||
[(ngModel)]="opts.defaultValues[i]">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Actual</th>
|
||||
<td class="actual"
|
||||
*ngFor="let heading of headings; let i = index;">
|
||||
<input type="text"
|
||||
[(ngModel)]="actualValues[i]">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
<div class="loading-spinner"
|
||||
*ngIf="isLoading && !googleApiService.isLoggedIn">
|
||||
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
|
||||
</div>
|
||||
|
||||
|
||||
<div mat-dialog-actions="">
|
||||
<button (click)="save()"
|
||||
*ngIf="actualValues.length > 0 && GoogleApi.isLoggedIn"
|
||||
type="button"
|
||||
mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn>
|
||||
<mat-icon>save</mat-icon>
|
||||
Save row
|
||||
</button>
|
||||
<button (click)="cancel()"
|
||||
type="button"
|
||||
class="md-raised">
|
||||
<mat-icon>close</mat-icon>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<form (submit)="save()">
|
||||
<div mat-dialog-content=""
|
||||
class="dialog-content">
|
||||
<collapsible class="help-collapsible"
|
||||
title="What is this and how does it work?"
|
||||
[initiallyExpanded]="opts.spreadsheetId">
|
||||
<div class="info">
|
||||
<p>This view allows you to export your worked time to a google sheet. You need to allow for your Google Spreadsheets to be accessed by Super Productivity. You also need to create a spreadsheet with a headings in the first row nad specify it's ID in the input field (<a external-link
|
||||
target="_blank"
|
||||
href="https://stackoverflow.com/questions/36061433/how-to-do-i-locate-a-google-spreadsheet-id">Ho to find the id of a spreadsheet?</a>).
|
||||
</p>
|
||||
<p>After successfully loading your spreadsheet a table will show up with 4 rows. The first row shows the heading you specified in the spreadsheet itself.</p>
|
||||
<p>The second row is for informational purposes and shows the last row from the spreadsheet.</p>
|
||||
<p>The forth row is a list of values you can directly enter save to the spreadsheet.</p>
|
||||
<p>The third row is there to automatically define some values for the forth row. There are several special strings you can enter into the cells:</p>
|
||||
</div>
|
||||
|
||||
<dl class="possible-properties">
|
||||
<dt>{{ '{' }}startTime}</dt>
|
||||
<dd>The time when you first used this app today. It's possible to round this via the options.</dd>
|
||||
|
||||
<dt>{{ '{' }}currentTime}</dt>
|
||||
<dd>The current time. Could be used for the for the end time of todays working day It's possible to round this via the options.</dd>
|
||||
|
||||
<dt>{{ '{' }}date}</dt>
|
||||
<dd>Todays date in standard format (mm/dd/yyyy)</dd>
|
||||
|
||||
<dt>{{ '{' }}date:DD/MM/YYYY} (example)</dt>
|
||||
<dd>Date with a custom date format string.</dd>
|
||||
|
||||
<dt>{{ '{' }}taskTitles}</dt>
|
||||
<dd>Comma separated (parent) task titles</dd>
|
||||
|
||||
<dt>{{ '{' }}subTaskTitles}</dt>
|
||||
<dd>Comma separated sub task titles</dd>
|
||||
|
||||
<dt>{{ '{' }}totalTime}</dt>
|
||||
<dd>The total time you spend working on your todays tasks.</dd>
|
||||
</dl>
|
||||
|
||||
<p>In addition to this there are several options you can use to modify the calculation of those values.</p>
|
||||
</collapsible>
|
||||
|
||||
<collapsible title="Options"
|
||||
class="options-collapsible"
|
||||
[initiallyExpanded]="opts.spreadsheetId">
|
||||
<mat-slide-toggle
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="opts.isAutoLogin">Auto-login and load data next time
|
||||
</mat-slide-toggle>
|
||||
<!--<mat-slide-toggle [(ngModel)]="optsname="".is.isAutoFocusEmpty">Auto-focus first empty field after loading table headings</mat-slide-toggle>-->
|
||||
<mat-slide-toggle
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="opts.isRoundWorkTimeUp"
|
||||
(change)="updateDefaults()">Always round work time up
|
||||
</mat-slide-toggle>
|
||||
<mat-form-field>
|
||||
<label>Round start time to</label>
|
||||
<mat-select (change)="updateDefaults()"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="opts.roundStartTimeTo">
|
||||
<mat-option><em>don't round</em></mat-option>
|
||||
<mat-option *ngFor="let roundOption of roundTimeOptions"
|
||||
[value]="roundOption.id">
|
||||
{{roundOption.title}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<label>Round end time to</label>
|
||||
<mat-select (change)="updateDefaults()"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="opts.roundEndTimeTo">
|
||||
<mat-option><em>don't round</em></mat-option>
|
||||
<mat-option *ngFor="let roundOption of roundTimeOptions"
|
||||
[value]="roundOption.id">
|
||||
{{roundOption.title}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<label>Round time worked to</label>
|
||||
<mat-select (change)="updateDefaults()"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="opts.roundWorkTimeTo">
|
||||
<mat-option><em>don't round</em></mat-option>
|
||||
<mat-option *ngFor="let roundOption of roundTimeOptions"
|
||||
[value]="roundOption.id">
|
||||
{{roundOption.title}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</collapsible>
|
||||
|
||||
<div>
|
||||
<button mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn
|
||||
*ngIf="!googleApiService.isLoggedIn"
|
||||
(click)="login()">Login
|
||||
</button>
|
||||
<button mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn
|
||||
*ngIf="googleApiService.isLoggedIn"
|
||||
(click)="logout()">Logout
|
||||
</button>
|
||||
<button mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn
|
||||
[disabled]="!opts.spreadsheetId"
|
||||
*ngIf="googleApiService.isLoggedIn"
|
||||
(click)="readSpreadsheet()">Read spreadsheet
|
||||
</button>
|
||||
<a mat-raised-button=""
|
||||
color="primary"
|
||||
*ngIf="googleApiService.isLoggedIn"
|
||||
external-link
|
||||
target="_blank"
|
||||
href="https://myaccount.google.com/permissions">Revoke permissions
|
||||
</a>
|
||||
<a mat-raised-button=""
|
||||
color="primary"
|
||||
external-link
|
||||
target="_blank"
|
||||
*ngIf="googleApiService.isLoggedIn && opts.spreadsheetId"
|
||||
href="https://docs.google.com/spreadsheets/d/{{opts.spreadsheetId}}">Edit spreadsheet
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<section *ngIf="googleApiService.isLoggedIn">
|
||||
<mat-form-field class="md-block">
|
||||
<label>Spreadsheet ID</label>
|
||||
<input type="text"
|
||||
matInput=""
|
||||
name="spreadsheetId"
|
||||
[(ngModel)]="opts.spreadsheetId">
|
||||
</mat-form-field>
|
||||
|
||||
<table class="export-input-table"
|
||||
*ngIf="headings">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="heading"
|
||||
*ngFor="let heading of headings"
|
||||
[innerHtml]="heading"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Last saved row</th>
|
||||
<td *ngFor="let col of lastRow">{{col}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default</th>
|
||||
<td class="default"
|
||||
*ngFor="let heading of headings; let i = index;">
|
||||
<input type="text"
|
||||
(change)="updateDefaults()"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[(ngModel)]="opts.defaultValues[i]">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Actual</th>
|
||||
<td class="actual"
|
||||
*ngFor="let heading of headings; let i = index;">
|
||||
<input type="text"
|
||||
name="actualValues"
|
||||
[(ngModel)]="actualValues[i]">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
<div mat-dialog-actions="">
|
||||
<button (click)="save()"
|
||||
*ngIf="actualValues.length > 0 && googleApiService.isLoggedIn"
|
||||
type="button"
|
||||
mat-raised-button=""
|
||||
color="primary"
|
||||
promise-btn>
|
||||
<mat-icon>save</mat-icon>
|
||||
Save row
|
||||
</button>
|
||||
<button (click)="cancel()"
|
||||
type="button"
|
||||
mat-raised-button="">
|
||||
<mat-icon>close</mat-icon>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
.help-collapsible {
|
||||
::ng-deep.collapsible-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.options-collapsible {
|
||||
::ng-deep .collapsible-title {
|
||||
font-size: 20px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.export-input-table {
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
min-width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.possible-properties {
|
||||
dt {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
dd {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
|
||||
mat-progress-spinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
mat-input-container {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
import { Duration, Moment } from 'moment';
|
||||
import { GoogleApiService } from '../google-api.service';
|
||||
import { SnackService } from '../../snack/snack.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dialog-google-export-time',
|
||||
|
|
@ -7,11 +11,275 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DialogGoogleExportTimeComponent implements OnInit {
|
||||
opts: any = {
|
||||
spreadsheetId: undefined,
|
||||
isAutoLogin: false,
|
||||
isAutoFocusEmpty: false,
|
||||
isRoundWorkTimeUp: undefined,
|
||||
roundStartTimeTo: undefined,
|
||||
roundEndTimeTo: undefined,
|
||||
roundWorkTimeTo: undefined,
|
||||
defaultValues: [
|
||||
''
|
||||
]
|
||||
};
|
||||
// $rootScope.r.uiHelper.timeSheetExportSettings;
|
||||
actualValues = [];
|
||||
isLoading = false;
|
||||
isLoggedIn = false;
|
||||
headings: string[] = [];
|
||||
lastRow: string[] = [];
|
||||
MISSING = {
|
||||
startedTimeToday: 'MISSING startedTimeToday',
|
||||
getTimeWorkedToday: 'MISSING getTimeWorkedToday',
|
||||
getToday: []
|
||||
};
|
||||
|
||||
constructor() {
|
||||
roundTimeOptions = [
|
||||
{id: 'QUARTER', title: 'full quarters'},
|
||||
{id: 'HALF', title: 'full half hours'},
|
||||
{id: 'HOUR', title: 'full hours'},
|
||||
];
|
||||
|
||||
|
||||
constructor(
|
||||
public googleApiService: GoogleApiService,
|
||||
private _snackService: SnackService,
|
||||
private _cd: ChangeDetectorRef
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.opts.isAutoLogin) {
|
||||
this.login()
|
||||
.then(() => {
|
||||
if (this.opts.spreadsheetId) {
|
||||
this.readSpreadsheet();
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.updateDefaults();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
// $mdDialog.hide();
|
||||
}
|
||||
|
||||
login() {
|
||||
this.isLoading = true;
|
||||
return this.googleApiService.login()
|
||||
.then(() => {
|
||||
this.isLoading = false;
|
||||
this.isLoggedIn = true;
|
||||
this._cd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
readSpreadsheet() {
|
||||
this.isLoading = true;
|
||||
this.headings = undefined;
|
||||
return this.googleApiService.getSpreadsheetHeadingsAndLastRow(this.opts.spreadsheetId)
|
||||
.then((data: any) => {
|
||||
this.headings = data.headings;
|
||||
this.lastRow = data.lastRow;
|
||||
this.updateDefaults();
|
||||
this.isLoading = false;
|
||||
this._cd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.isLoading = false;
|
||||
return this.googleApiService.logout()
|
||||
.then(() => {
|
||||
this.isLoading = false;
|
||||
this.isLoggedIn = false;
|
||||
this._cd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
this.isLoading = true;
|
||||
const arraysEqual = (arr1, arr2) => {
|
||||
if (arr1.length !== arr2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = arr1.length; i--;) {
|
||||
if (arr1[i] !== arr2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (arraysEqual(this.actualValues, this.lastRow)) {
|
||||
this._snackService.open('Current values and the last saved row have equal values, that is probably not what you want.');
|
||||
} else {
|
||||
|
||||
this.googleApiService.appendRow(this.opts.spreadsheetId, this.actualValues)
|
||||
.then(() => {
|
||||
this._snackService.open({
|
||||
message: 'Row successfully appended',
|
||||
type: 'SUCCESS'
|
||||
});
|
||||
|
||||
// $mdDialog.hide();
|
||||
// $rootScope.r.currentSession.isTimeSheetExported = true;
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateDefaults() {
|
||||
this.opts.defaultValues.forEach((val, index) => {
|
||||
this.actualValues[index] = this.replaceVals(val);
|
||||
});
|
||||
}
|
||||
|
||||
replaceVals(defaultVal: string): string {
|
||||
if (!defaultVal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dVal = defaultVal.trim();
|
||||
|
||||
if (dVal.match(/\{date:/)) {
|
||||
return this.getCustomDate(dVal);
|
||||
}
|
||||
|
||||
switch (dVal) {
|
||||
case '{startTime}':
|
||||
return this.getStartTime();
|
||||
case '{currentTime}':
|
||||
return this.getCurrentTime();
|
||||
case '{date}':
|
||||
return moment().format('MM/DD/YYYY');
|
||||
case '{taskTitles}':
|
||||
return this.getTaskTitles();
|
||||
case '{subTaskTitles}':
|
||||
return this.getSubTaskTitles();
|
||||
case '{totalTime}':
|
||||
return this.getTotalTimeWorked();
|
||||
default:
|
||||
return dVal;
|
||||
}
|
||||
}
|
||||
|
||||
private roundDuration(value: Duration, roundTo, isRoundUp): Duration {
|
||||
let rounded;
|
||||
|
||||
switch (roundTo) {
|
||||
case 'QUARTER':
|
||||
rounded = Math.round(value.asMinutes() / 15) * 15;
|
||||
if (isRoundUp) {
|
||||
rounded = Math.ceil(value.asMinutes() / 15) * 15;
|
||||
}
|
||||
return moment.duration({minutes: rounded});
|
||||
|
||||
case 'HALF':
|
||||
rounded = Math.round(value.asMinutes() / 30) * 30;
|
||||
if (isRoundUp) {
|
||||
rounded = Math.ceil(value.asMinutes() / 30) * 30;
|
||||
}
|
||||
return moment.duration({minutes: rounded});
|
||||
|
||||
case 'HOUR':
|
||||
rounded = Math.round(value.asMinutes() / 60) * 60;
|
||||
if (isRoundUp) {
|
||||
rounded = Math.ceil(value.asMinutes() / 60) * 60;
|
||||
}
|
||||
return moment.duration({minutes: rounded});
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private roundTime(value: Moment, roundTo, isRoundUp = false): Moment {
|
||||
let rounded;
|
||||
|
||||
switch (roundTo) {
|
||||
case 'QUARTER':
|
||||
rounded = Math.round(value.minute() / 15) * 15;
|
||||
if (isRoundUp) {
|
||||
rounded = Math.ceil(value.minute() / 15) * 15;
|
||||
}
|
||||
return value.minute(rounded).second(0);
|
||||
|
||||
case 'HALF':
|
||||
rounded = Math.round(value.minute() / 30) * 30;
|
||||
if (isRoundUp) {
|
||||
rounded = Math.ceil(value.minute() / 30) * 30;
|
||||
}
|
||||
return value.minute(rounded).second(0);
|
||||
|
||||
case 'HOUR':
|
||||
rounded = Math.round(value.minute() / 60) * 60;
|
||||
if (isRoundUp) {
|
||||
rounded = Math.ceil(value.minute() / 60) * 60;
|
||||
}
|
||||
return value.minute(rounded).second(0);
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private getCustomDate(dVal: string): string {
|
||||
const dateFormatStr = dVal
|
||||
.replace('{date:', '')
|
||||
.replace('}', '')
|
||||
.trim();
|
||||
return moment().format(dateFormatStr);
|
||||
}
|
||||
|
||||
private getStartTime() {
|
||||
const val = moment(this.MISSING.startedTimeToday);
|
||||
const roundTo = this.opts.roundStartTimeTo;
|
||||
return this.roundTime(val, roundTo)
|
||||
.format('HH:mm');
|
||||
}
|
||||
|
||||
private getCurrentTime(): string {
|
||||
const val = moment();
|
||||
const roundTo = this.opts.roundEndTimeTo;
|
||||
|
||||
return this.roundTime(val, roundTo)
|
||||
.format('HH:mm');
|
||||
}
|
||||
|
||||
private getTotalTimeWorked(): string {
|
||||
const val = moment.duration(this.MISSING.getTimeWorkedToday);
|
||||
|
||||
const roundTo = this.opts.roundWorkTimeTo;
|
||||
const dur = this.roundDuration(val, roundTo, this.opts.isRoundWorkTimeUp) as any;
|
||||
return dur.format('HH:mm');
|
||||
}
|
||||
|
||||
private getTaskTitles(): string {
|
||||
const tasks = this.MISSING.getToday;
|
||||
let titleStr = '';
|
||||
tasks.forEach((task) => {
|
||||
titleStr += task.title + ', ';
|
||||
});
|
||||
return titleStr.substring(0, titleStr.length - 2);
|
||||
}
|
||||
|
||||
private getSubTaskTitles(): string {
|
||||
const tasks = this.MISSING.getToday;
|
||||
let titleStr = '';
|
||||
tasks.forEach((task) => {
|
||||
if (task.subTasks) {
|
||||
task.subTasks.forEach((subTask) => {
|
||||
titleStr += subTask.title + ', ';
|
||||
});
|
||||
} else {
|
||||
titleStr += task.title + ', ';
|
||||
}
|
||||
});
|
||||
return titleStr.substring(0, titleStr.length - 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { GOOGLE_DEFAULT_FIELDS_FOR_DRIVE, GOOGLE_DISCOVERY_DOCS, GOOGLE_SCOPES,
|
|||
import * as moment from 'moment';
|
||||
import { IS_ELECTRON } from '../../app.constants';
|
||||
import { MultiPartBuilder } from './util/multi-part-builder';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpRequest } from '@angular/common/http';
|
||||
import { SnackService } from '../snack/snack.service';
|
||||
import { SnackType } from '../snack/snack.model';
|
||||
|
||||
|
|
@ -138,9 +138,9 @@ export class GoogleApiService {
|
|||
return this._requestWrapper(new Promise((resolve, reject) => {
|
||||
this.getSpreadsheetData(spreadsheetId, 'A1:Z99')
|
||||
.then((response: any) => {
|
||||
const range = response.result || response.data;
|
||||
const range = response.result || response.data || response;
|
||||
|
||||
if (range.values && range.values[0]) {
|
||||
if (range && range.values && range.values[0]) {
|
||||
resolve({
|
||||
headings: range.values[0],
|
||||
lastRow: range.values[range.values.length - 1],
|
||||
|
|
@ -364,8 +364,8 @@ export class GoogleApiService {
|
|||
});
|
||||
}
|
||||
|
||||
private _mapHttp(params: any): Promise<any> {
|
||||
return this._http.request(params).toPromise();
|
||||
private _mapHttp(p: HttpRequest | any): Promise<any> {
|
||||
return this._http[p.method.toLowerCase()](p.url, p).toPromise();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ export class SnackService {
|
|||
constructor(private _store$: Store<any>) {
|
||||
}
|
||||
|
||||
open(params: SnackParams) {
|
||||
open(params: SnackParams | string) {
|
||||
if (typeof params === 'string') {
|
||||
params = {message: params};
|
||||
}
|
||||
this._store$.dispatch(new SnackOpen(params));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@
|
|||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"types": [
|
||||
"@types/moment-duration-format"
|
||||
],
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue