mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	add configuration option for changing date display in paperless #325
This commit is contained in:
		| @@ -13,7 +13,7 @@ import { DocumentTypeListComponent } from './components/manage/document-type-lis | ||||
| import { LogsComponent } from './components/manage/logs/logs.component'; | ||||
| import { SettingsComponent } from './components/manage/settings/settings.component'; | ||||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||||
| import { DatePipe } from '@angular/common'; | ||||
| import { DatePipe, registerLocaleData } from '@angular/common'; | ||||
| import { NotFoundComponent } from './components/not-found/not-found.component'; | ||||
| import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'; | ||||
| import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component'; | ||||
| @@ -59,6 +59,15 @@ import { SelectDialogComponent } from './components/common/select-dialog/select- | ||||
| import { NgSelectModule } from '@ng-select/ng-select'; | ||||
| import { NumberComponent } from './components/common/input/number/number.component'; | ||||
| import { SafePipe } from './pipes/safe.pipe'; | ||||
| import { CustomDatePipe } from './pipes/custom-date.pipe'; | ||||
|  | ||||
| import localeFr from '@angular/common/locales/fr'; | ||||
| import localeNl from '@angular/common/locales/nl'; | ||||
| import localeDe from '@angular/common/locales/de'; | ||||
|  | ||||
| registerLocaleData(localeFr) | ||||
| registerLocaleData(localeNl) | ||||
| registerLocaleData(localeDe) | ||||
|  | ||||
| @NgModule({ | ||||
|   declarations: [ | ||||
| @@ -108,7 +117,8 @@ import { SafePipe } from './pipes/safe.pipe'; | ||||
|     MetadataCollapseComponent, | ||||
|     SelectDialogComponent, | ||||
|     NumberComponent, | ||||
|     SafePipe | ||||
|     SafePipe, | ||||
|     CustomDatePipe | ||||
|   ], | ||||
|   imports: [ | ||||
|     BrowserModule, | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|     </thead> | ||||
|     <tbody> | ||||
|       <tr *ngFor="let doc of documents" routerLink="/documents/{{doc.id}}"> | ||||
|         <td>{{doc.created | date}}</td> | ||||
|         <td>{{doc.created | customDate}}</td> | ||||
|         <td>{{doc.title | documentTitle}}<app-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ml-1"></app-tag> | ||||
|       </tr> | ||||
|     </tbody> | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export class SavedViewWidgetComponent implements OnInit { | ||||
|     } else { | ||||
|       this.list.load(this.savedView) | ||||
|       this.router.navigate(["documents"]) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -85,11 +85,11 @@ | ||||
|                             <tbody> | ||||
|                                 <tr> | ||||
|                                     <td i18n>Date modified</td> | ||||
|                                     <td>{{document.modified | date:'medium'}}</td> | ||||
|                                     <td>{{document.modified | customDate}}</td> | ||||
|                                 </tr> | ||||
|                                 <tr> | ||||
|                                     <td i18n>Date added</td> | ||||
|                                     <td>{{document.added | date:'medium'}}</td> | ||||
|                                     <td>{{document.added | customDate}}</td> | ||||
|                                 </tr> | ||||
|                                 <tr> | ||||
|                                     <td i18n>Media filename</td> | ||||
|   | ||||
| @@ -64,7 +64,7 @@ | ||||
|             <ngb-progressbar [type]="searchScoreClass" [value]="searchScore" class="search-score-bar mx-2" [max]="1"></ngb-progressbar> | ||||
|           </div> | ||||
|  | ||||
|           <small class="text-muted" [class.ml-auto]="!searchScore" i18n>Created: {{document.created | date}}</small> | ||||
|           <small class="text-muted" [class.ml-auto]="!searchScore" i18n>Created: {{document.created | customDate}}</small> | ||||
|         </div> | ||||
|  | ||||
|       </div> | ||||
|   | ||||
| @@ -50,7 +50,7 @@ | ||||
|             </svg> | ||||
|           </a> | ||||
|         </div> | ||||
|         <small class="text-muted pl-1">{{document.created | date}}</small> | ||||
|         <small class="text-muted pl-1">{{document.created | customDate:'shortDate'}}</small> | ||||
|       </div> | ||||
|  | ||||
|     </div> | ||||
|   | ||||
| @@ -160,10 +160,10 @@ | ||||
|         </ng-container> | ||||
|       </td> | ||||
|       <td> | ||||
|         {{d.created | date}} | ||||
|         {{d.created | customDate}} | ||||
|       </td> | ||||
|       <td class="d-none d-xl-table-cell"> | ||||
|         {{d.added | date}} | ||||
|         {{d.added | customDate}} | ||||
|       </td> | ||||
|     </tr> | ||||
|   </tbody> | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|       <td scope="row">{{ correspondent.name }}</td> | ||||
|       <td scope="row">{{ getMatching(correspondent) }}</td> | ||||
|       <td scope="row">{{ correspondent.document_count }}</td> | ||||
|       <td scope="row">{{ correspondent.last_correspondence | date }}</td> | ||||
|       <td scope="row">{{ correspondent.last_correspondence | customDate }}</td> | ||||
|         <td scope="row"> | ||||
|           <div class="btn-group"> | ||||
|             <button class="btn btn-sm btn-outline-secondary" (click)="filterDocuments(correspondent)"> | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|   <p | ||||
|     class="m-0 p-0 log-entry-{{log.level}}" | ||||
|     *ngFor="let log of logs"> | ||||
|       {{log.created | date:'short'}} | ||||
|       {{log.created | customDate:'short'}} | ||||
|       {{getLevelText(log.level)}} | ||||
|       {{log.message}} | ||||
|   </p> | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|           <div class="col"> | ||||
|  | ||||
|             <select class="form-control" formControlName="displayLanguage"> | ||||
|               <option *ngFor="let lang of languages" [ngValue]="lang.code">{{lang.name}}</option> | ||||
|               <option *ngFor="let lang of displayLanguageOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> ({{lang.englishName}})</span></option> | ||||
|             </select> | ||||
|  | ||||
|             <small class="form-text text-muted" i18n>You need to reload the page after applying a new language.</small> | ||||
| @@ -27,6 +27,41 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="form-row form-group"> | ||||
|           <div class="col-md-3 col-form-label"> | ||||
|             <span i18n>Date display</span> | ||||
|           </div> | ||||
|           <div class="col"> | ||||
|  | ||||
|             <select class="form-control" formControlName="dateLocale"> | ||||
|               <option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> ({{today | date:'shortDate':null:lang.code}})</span></option> | ||||
|             </select> | ||||
|  | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="form-row form-group"> | ||||
|           <div class="col-md-3 col-form-label"> | ||||
|             <span i18n>Date format</span> | ||||
|           </div> | ||||
|           <div class="col"> | ||||
|  | ||||
|             <div class="custom-control custom-radio"> | ||||
|               <input type="radio" id="dateFormatShort" name="dateFormat" class="custom-control-input" formControlName="dateFormat" value="shortDate"> | ||||
|               <label class="custom-control-label" for="dateFormatShort" i18n>Short: {{today | customDate:'shortDate'}}</label> | ||||
|             </div> | ||||
|             <div class="custom-control custom-radio"> | ||||
|               <input type="radio" id="dateFormatMedium" name="dateFormat" class="custom-control-input" formControlName="dateFormat" value="mediumDate"> | ||||
|               <label class="custom-control-label" for="dateFormatMedium" i18n>Medium: {{today | customDate:'mediumDate'}}</label> | ||||
|             </div> | ||||
|             <div class="custom-control custom-radio"> | ||||
|               <input type="radio" id="dateFormatLong" name="dateFormat" class="custom-control-input" formControlName="dateFormat" value="longDate"> | ||||
|               <label class="custom-control-label" for="dateFormatLong" i18n>Long: {{today | customDate:'longDate'}}</label> | ||||
|             </div> | ||||
|  | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="form-row form-group"> | ||||
|           <div class="col-md-3 col-form-label"> | ||||
|             <span i18n>Items per page</span> | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import { FormControl, FormGroup } from '@angular/forms'; | ||||
| import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; | ||||
| import { DocumentListViewService } from 'src/app/services/document-list-view.service'; | ||||
| import { SavedViewService } from 'src/app/services/rest/saved-view.service'; | ||||
| import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; | ||||
| import { LanguageOption, SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; | ||||
| import { ToastService } from 'src/app/services/toast.service'; | ||||
|  | ||||
| @Component({ | ||||
| @@ -23,7 +23,9 @@ export class SettingsComponent implements OnInit { | ||||
|     'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)), | ||||
|     'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)), | ||||
|     'savedViews': this.savedViewGroup, | ||||
|     'displayLanguage': new FormControl(this.settings.getLanguage()) | ||||
|     'displayLanguage': new FormControl(this.settings.getLanguage()), | ||||
|     'dateLocale': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_LOCALE)), | ||||
|     'dateFormat': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_FORMAT)), | ||||
|   }) | ||||
|  | ||||
|   savedViews: PaperlessSavedView[] | ||||
| @@ -64,14 +66,24 @@ export class SettingsComponent implements OnInit { | ||||
|     this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem) | ||||
|     this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString()) | ||||
|     this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer) | ||||
|     this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale) | ||||
|     this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat) | ||||
|     this.settings.setLanguage(this.settingsForm.value.displayLanguage) | ||||
|     this.documentListViewService.updatePageSize() | ||||
|     this.settings.updateDarkModeSettings() | ||||
|     this.toastService.showInfo($localize`Settings saved successfully.`) | ||||
|   } | ||||
|  | ||||
|   get languages() { | ||||
|     return this.settings.getLanguageOptions() | ||||
|   get displayLanguageOptions(): LanguageOption[] { | ||||
|     return [{code: "", name: $localize`Use system language`}].concat(this.settings.getLanguageOptions()) | ||||
|   } | ||||
|  | ||||
|   get dateLocaleOptions(): LanguageOption[] { | ||||
|     return [{code: "", name: $localize`Use same as display language`}].concat(this.settings.getLanguageOptions()) | ||||
|   } | ||||
|  | ||||
|   get today() { | ||||
|     return new Date() | ||||
|   } | ||||
|  | ||||
|   saveSettings() { | ||||
|   | ||||
							
								
								
									
										8
									
								
								src-ui/src/app/pipes/custom-date.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src-ui/src/app/pipes/custom-date.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import { CustomDatePipe } from './custom-date.pipe'; | ||||
|  | ||||
| describe('CustomDatePipe', () => { | ||||
|   it('create an instance', () => { | ||||
|     const pipe = new CustomDatePipe(); | ||||
|     expect(pipe).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										19
									
								
								src-ui/src/app/pipes/custom-date.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src-ui/src/app/pipes/custom-date.pipe.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import { DatePipe } from '@angular/common'; | ||||
| import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; | ||||
| import { SettingsService, SETTINGS_KEYS } from '../services/settings.service'; | ||||
|  | ||||
| @Pipe({ | ||||
|   name: 'customDate' | ||||
| }) | ||||
| export class CustomDatePipe extends DatePipe implements PipeTransform { | ||||
|  | ||||
|   constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) { | ||||
|     super(settings.get(SETTINGS_KEYS.DATE_LOCALE) || locale) | ||||
|  | ||||
|   } | ||||
|  | ||||
|   transform(value: any, format?: string, timezone?: string, locale?: string): string | null { | ||||
|     return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale) | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -9,13 +9,21 @@ export interface PaperlessSettings { | ||||
|   default: any | ||||
| } | ||||
|  | ||||
| export interface LanguageOption { | ||||
|   code: string, | ||||
|   name: string, | ||||
|   englishName?: string | ||||
| } | ||||
|  | ||||
| export const SETTINGS_KEYS = { | ||||
|   BULK_EDIT_CONFIRMATION_DIALOGS: 'general-settings:bulk-edit:confirmation-dialogs', | ||||
|   BULK_EDIT_APPLY_ON_CLOSE: 'general-settings:bulk-edit:apply-on-close', | ||||
|   DOCUMENT_LIST_SIZE: 'general-settings:documentListSize', | ||||
|   DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system', | ||||
|   DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled', | ||||
|   USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer' | ||||
|   USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer', | ||||
|   DATE_LOCALE: 'general-settings:date-display:date-locale', | ||||
|   DATE_FORMAT: 'general-settings:date-display:date-format' | ||||
| } | ||||
|  | ||||
| const SETTINGS: PaperlessSettings[] = [ | ||||
| @@ -24,7 +32,9 @@ const SETTINGS: PaperlessSettings[] = [ | ||||
|   {key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50}, | ||||
|   {key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true}, | ||||
|   {key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false}, | ||||
|   {key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false} | ||||
|   {key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false}, | ||||
|   {key: SETTINGS_KEYS.DATE_LOCALE, type: "string", default: "de"}, | ||||
|   {key: SETTINGS_KEYS.DATE_FORMAT, type: "string", default: "mediumDate"} | ||||
| ] | ||||
|  | ||||
| @Injectable({ | ||||
| @@ -59,13 +69,12 @@ export class SettingsService { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   getLanguageOptions() { | ||||
|   getLanguageOptions(): LanguageOption[] { | ||||
|     return [ | ||||
|       {code: "", name: $localize`Use system language`}, | ||||
|       {code: "en-us", name: `${$localize`English`} (English)`}, | ||||
|       {code: "de", name: `${$localize`German`} (German)`}, | ||||
|       {code: "nl", name: `${$localize`Dutch`} (Dutch)`}, | ||||
|       {code: "fr", name: `${$localize`French`} (French)`} | ||||
|       {code: "en-us", name: $localize`English`, englishName: "English"}, | ||||
|       {code: "de", name: $localize`German`, englishName: "German"}, | ||||
|       {code: "nl", name: $localize`Dutch`, englishName: "Dutch"}, | ||||
|       {code: "fr", name: $localize`French`, englishName: "French"} | ||||
|     ] | ||||
|   } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler