diff --git a/package-lock.json b/package-lock.json index d8f4cba..7010c3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1364,6 +1364,16 @@ "tslib": "^1.10.0" } }, + "@ionic/storage": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ionic/storage/-/storage-2.2.0.tgz", + "integrity": "sha512-2pszrzmI+fAar2Rx0WmJDVpc15D1k5tvLkB49NLYWJ2pOMaO/3/vp7mg/mEbg3rdsPE9FRbYI6vdKjQ2pP1EWA==", + "requires": { + "localforage": "1.7.1", + "localforage-cordovasqlitedriver": "1.7.0", + "tslib": "^1.7.1" + } + }, "@ngtools/webpack": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.1.3.tgz", @@ -5056,8 +5066,7 @@ "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, "import-cwd": { "version": "2.1.0", @@ -6691,6 +6700,32 @@ "json5": "^1.0.1" } }, + "localforage": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.1.tgz", + "integrity": "sha1-5JJ+BCMCuGTbMPMhHxO1xvDell0=", + "requires": { + "lie": "3.1.1" + }, + "dependencies": { + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "requires": { + "immediate": "~3.0.5" + } + } + } + }, + "localforage-cordovasqlitedriver": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/localforage-cordovasqlitedriver/-/localforage-cordovasqlitedriver-1.7.0.tgz", + "integrity": "sha1-i5OVd1nuaI06WNW6fAR39sy1ODg=", + "requires": { + "localforage": ">=1.5.0" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", diff --git a/package.json b/package.json index 86564bb..76db54b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@ionic-native/splash-screen": "^5.0.0", "@ionic-native/status-bar": "^5.0.0", "@ionic/angular": "^4.7.1", + "@ionic/storage": "^2.2.0", "cordova-android": "8.0.0", "core-js": "^2.5.4", "rxjs": "~6.5.1", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 515e6ce..046b24d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -6,6 +6,7 @@ const routes: Routes = [ { path: 'onboarding', loadChildren: './onboarding/onboarding.module#OnboardingPageModule' }, { path: 'login', loadChildren: './login/login.module#LoginPageModule' }, { path: 'malls', loadChildren: './malls/malls.module#MallsPageModule' }, + { path: 'mall-details', loadChildren: './mall-details/mall-details.module#MallDetailsPageModule' }, ]; @NgModule({ diff --git a/src/app/app.component.html b/src/app/app.component.html index 13b9677..99a9c08 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 65b00ab..015c848 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,23 +5,25 @@ import { SplashScreen } from '@ionic-native/splash-screen/ngx'; import { StatusBar } from '@ionic-native/status-bar/ngx'; @Component({ - selector: 'app-root', - templateUrl: 'app.component.html', - styleUrls: ['app.component.scss'] + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'] }) export class AppComponent { - constructor( - private platform: Platform, - private splashScreen: SplashScreen, - private statusBar: StatusBar - ) { - this.initializeApp(); - } + show_menu = false; + + constructor( + private platform: Platform, + private splashScreen: SplashScreen, + private statusBar: StatusBar + ) { + this.initializeApp(); + } - initializeApp() { - this.platform.ready().then(() => { - this.statusBar.styleDefault(); - this.splashScreen.hide(); - }); - } + initializeApp() { + this.platform.ready().then(() => { + this.statusBar.styleDefault(); + this.splashScreen.hide(); + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0d78bbe..94ea781 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -6,6 +6,9 @@ import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; import { SplashScreen } from '@ionic-native/splash-screen/ngx'; import { StatusBar } from '@ionic-native/status-bar/ngx'; +// Modules import for Modals + +// Services import import { MallService } from './services/mall.service'; import { AppComponent } from './app.component'; @@ -16,7 +19,11 @@ import { environment } from '../environments/environment'; @NgModule({ declarations: [AppComponent], entryComponents: [], - imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })], + imports: [ + BrowserModule, + IonicModule.forRoot(), + AppRoutingModule, + ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })], providers: [ StatusBar, SplashScreen, diff --git a/src/app/mall-details/mall-details.module.ts b/src/app/mall-details/mall-details.module.ts new file mode 100644 index 0000000..bd53ec2 --- /dev/null +++ b/src/app/mall-details/mall-details.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { Routes, RouterModule } from '@angular/router'; + +import { IonicModule } from '@ionic/angular'; + +import { MallDetailsPage } from './mall-details.page'; + +const routes: Routes = [ + { + path: '', + component: MallDetailsPage + } +]; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + RouterModule.forChild(routes) + ], + declarations: [MallDetailsPage] +}) +export class MallDetailsPageModule {} diff --git a/src/app/mall-details/mall-details.page.html b/src/app/mall-details/mall-details.page.html new file mode 100644 index 0000000..79e7512 --- /dev/null +++ b/src/app/mall-details/mall-details.page.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + {{ mall_details.name }} + + {{ mall_details.rating }} (2000) + {{ mall_details.distance }} km + + {{ mall_details.description }} + + + + + FOOD OFFERS + SHOPPING OFFERS + + + diff --git a/src/app/mall-details/mall-details.page.scss b/src/app/mall-details/mall-details.page.scss new file mode 100644 index 0000000..622f00b --- /dev/null +++ b/src/app/mall-details/mall-details.page.scss @@ -0,0 +1,137 @@ +.upfold-holder { + position: relative; + height: 45vh; + width: 100%; + padding-top: 30px; + margin-bottom: -25px; + + .icons-holder { + position: relative; + display: flex; + width: 90%; + margin: 0 auto; + justify-content: space-between; + + &.navigate-button { + position: absolute; + left: 86%; + bottom: 30px; + width: auto; + } + + button { + background-color: white; + color: var(--brand-blue); + font-size: 18px; + padding: 5px; + border-radius: 50%; + height: 30px; + width: 30px; + display: block; + margin-bottom: 10px; + box-shadow: 0px 0px 5px var(--brand-grey); + + &.active { + background-color: var(--brand-blue); + color: white; + } + } + } + + img { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 100%; + object-fit: cover; + filter: brightness(80%); + } +} + +.card-holder { + background-color: transparent; + position: relative; + + .card { + box-shadow: 0px 0px 5px var(--brand-grey); + width: 90%; + margin: 0 auto; + border-radius: 10px; + overflow: hidden; + padding: 20px; + background-color: white; + + h3 { + color: var(--brand-dark-grey); + font-size: 20px; + letter-spacing: 0.5px; + font-weight: 600; + } + + .stats-holder { + display: flex; + justify-content: space-between; + color: var(--brand-blue); + font-size: 10px; + font-weight: bold; + margin: 15px 0; + + span { + color: var(--brand-grey); + } + } + + p { + margin: 0 auto; + font-size: 11px; + line-height: 1.5; + color: var(--brand-grey); + } + } +} + +.tabs-holder { + display: flex; + width: 90%; + margin: 20px auto; + overflow: hidden; + border-radius: 30px; + box-shadow: 0px 0px 5px var(--brand-grey); + position: relative; + + &::before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 50%; + background-color: var(--brand-blue); + border-radius: 30px; + transform: translateX(0%); + transition: transform 0.3s; + } + + &.right { + &::before { + transform: translateX(100%); + } + } + + button { + position: relative; + background-color: transparent; + color: var(--brand-blue); + font-size: 12px; + letter-spacing: 0.5px; + height: 40px; + width: 50%; + font-weight: 500; + transition: color 0.3s; + + &.active { + color: white; + } + } +} diff --git a/src/app/mall-details/mall-details.page.spec.ts b/src/app/mall-details/mall-details.page.spec.ts new file mode 100644 index 0000000..0ba18ac --- /dev/null +++ b/src/app/mall-details/mall-details.page.spec.ts @@ -0,0 +1,27 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MallDetailsPage } from './mall-details.page'; + +describe('MallDetailsPage', () => { + let component: MallDetailsPage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MallDetailsPage ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MallDetailsPage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/mall-details/mall-details.page.ts b/src/app/mall-details/mall-details.page.ts new file mode 100644 index 0000000..34f46a5 --- /dev/null +++ b/src/app/mall-details/mall-details.page.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Mall, MallService } from '../services/mall.service'; + +@Component({ + selector: 'app-mall-details', + templateUrl: './mall-details.page.html', + styleUrls: ['./mall-details.page.scss'], +}) +export class MallDetailsPage implements OnInit { + mall_details: Mall; + selected_tab: string = 'food'; + + constructor( + private mallService: MallService, + private route: ActivatedRoute, + private router: Router + ) { } + + ngOnInit() { + let mall_id = this.route.snapshot.paramMap.get('mall_id'); + + this.mallService.getMallByID(mall_id).then((data: Mall) => { + this.mall_details = data; + }); + } + +} diff --git a/src/app/malls/malls.page.html b/src/app/malls/malls.page.html index 1d7f5fb..72fa9fe 100644 --- a/src/app/malls/malls.page.html +++ b/src/app/malls/malls.page.html @@ -1,7 +1,5 @@ - - Find malls near you @@ -22,14 +20,14 @@ - + {{ mall.name }} {{ mall.description }} - - {{ offer.name }}: {{ offer.offer.length }} + + {{ offer.name }}: {{ offer.offers.length }} @@ -72,14 +70,14 @@ - + {{ mall.name }} {{ mall.description }} - - {{ offer.name }}: {{ offer.offer.length }} + + {{ offer.name }}: {{ offer.offers.length }} @@ -119,14 +117,14 @@ - + {{ mall.name }} {{ mall.description }} - - {{ offer.name }}: {{ offer.offer.length }} + + {{ offer.name }}: {{ offer.offers.length }} @@ -153,23 +151,4 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/app/malls/malls.page.scss b/src/app/malls/malls.page.scss index be77dbe..7474ac2 100644 --- a/src/app/malls/malls.page.scss +++ b/src/app/malls/malls.page.scss @@ -116,6 +116,10 @@ ion-icon { color: var(--brand-grey); font-size: 16px; + + &.active { + color: var(--brand-blue); + } } } @@ -219,131 +223,6 @@ } } - -.menu-icon-holder { - background-color: #005cbe; - color: white; - position: fixed; - bottom: -60px; - box-shadow: 0px 0px 5px var(--brand-blue); - width: 100px; - height: 100px; - text-align: center; - left: calc((100% - 100px)/2); - border-radius: 50%; - padding-top: 7px; - z-index: 1; - transition: transform 0.5s; - transform: translateY(0); - opacity: 0.8; - - &.inactive { - transform: translateY(100px); - } - - ion-icon { - font-size: 30px; - } -} - -.close-icon-holder { - background-color: white; - width: 70%; - position: fixed; - left: calc((100% - 70%) / 2); - height: 100px; - border-radius: 50%; - color: var(--brand-blue); - text-align: center; - z-index: 2; - bottom: -60px; - padding-top: 10px; - transition: transform 0.3s; - transform: scale(0); - transition-delay: 0.7s; - - &.active { - transform: scale(1); - } - - ion-icon { - font-size: 25px; - } -} - -.menu-items-holder { - background-image: url('../../assets/custom/background-2.svg'); - background-size: cover; - background-repeat: no-repeat; - background-position: left top; - border-radius: 50%; - position: fixed; - left: calc((100% - 150%) / 2); - bottom: -150px; - z-index: 1; - height: 300px; - width: 150%; - padding: 20px 30%; - display: flex; - justify-content: space-between; - align-items: flex-start; - transition: transform 0.5s; - transform: scale(0); - transition-delay: 0.3s; - - &.active { - transform: scale(1); - } - - button { - background-color: white; - height: 50px; - width: 50px; - border-radius: 50%; - font-size: 20px; - position: relative; - - &:first-child { - transform: translateY(40px); - } - - &:nth-child(2) { - transform: translateY(20px); - } - - &:nth-child(3) { - transform: translateY(10px); - } - - &:last-child { - transform: translateY(40px); - } - - &:nth-child(4) { - transform: translateY(20px); - } - } -} - -.overlay { - position: fixed; - top: 0; - left: 0; - height: 100vh; - width: 100vw; - pointer-events: none; - background-color: black; - opacity: 0; - z-index: 1; - transition: opacity 0.5s; - - &.active { - pointer-events: all; - opacity: 0.5; - } -} - - .food-types-holder{ display: flex; width: 100%; diff --git a/src/app/malls/malls.page.ts b/src/app/malls/malls.page.ts index daf6a78..93b7d45 100644 --- a/src/app/malls/malls.page.ts +++ b/src/app/malls/malls.page.ts @@ -1,5 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { MallService } from '../services/mall.service'; +import { MallService, Mall } from '../services/mall.service'; +import { MallDetailsPage } from '../mall-details/mall-details.page'; +import { Router } from '@angular/router'; @Component({ selector: 'app-malls', @@ -7,19 +9,25 @@ import { MallService } from '../services/mall.service'; styleUrls: ['./malls.page.scss'], }) export class MallsPage implements OnInit { - selected_tab = 'you'; - show_menu = false; - malls = []; + selected_tab: string = 'you'; + malls: Array; - constructor(private mallService: MallService) { } + constructor( + private mallService: MallService, + private router: Router + ) { } ngOnInit() { } ionViewDidEnter() { - this.mallService.getAllMalls().then((data: Array) => { + this.mallService.getAllMalls().then((data: Array) => { this.malls = data; }); } + showMallDetails(mall) { + this.router.navigate(['/mall-details', { mall_id: mall.id }]); + } + } diff --git a/src/app/models/mall.ts b/src/app/models/mall.ts new file mode 100644 index 0000000..bcafd27 --- /dev/null +++ b/src/app/models/mall.ts @@ -0,0 +1,2 @@ +export class Mall { +} diff --git a/src/app/models/offer.ts b/src/app/models/offer.ts new file mode 100644 index 0000000..bfb46a9 --- /dev/null +++ b/src/app/models/offer.ts @@ -0,0 +1,30 @@ +export class Offer { + name: string; + description: string; + coupon_code: string; + + constructor(initializationObject: any) { + + // Check if object has the required fields + if (!initializationObject.hasOwnProperty('name')) { + throw new Error('The offer needs a name'); + } + + if (!initializationObject.hasOwnProperty('description')) { + throw new Error('The offer needs a description'); + } + + if (!initializationObject.hasOwnProperty('coupon_code')) { + throw new Error('The offer needs a coupon_code'); + } + + this.name = initializationObject.name; + this.description = initializationObject.description; + this.coupon_code = initializationObject.coupon_code; + } +} + +export type OfferCollection = { + name: string, + offers: Array, +}; diff --git a/src/app/services/mall.service.ts b/src/app/services/mall.service.ts index 422aafe..32a94bf 100644 --- a/src/app/services/mall.service.ts +++ b/src/app/services/mall.service.ts @@ -1,79 +1,119 @@ import { Injectable } from '@angular/core'; +import { Offer, OfferCollection } from '../models/offer'; + +type CoOrdinates = { + latitude: number, + longitude: number, +}; + +type Outlet = { + id: string, + image_url?: string, + name: string, + type: string, + offers: Array, + is_bookmarked: boolean, + rating: number, +}; + +export type Mall = { + id: string, + name: string, + is_bookmarked: boolean, + is_archived: boolean, + image_url?: string, + description: string, + offer_collection: Array, + outlets: Array, + rating: number, + distance: number, + location: CoOrdinates, +}; @Injectable({ providedIn: 'root' }) export class MallService { - malls = [{ - id: '00001', - name: 'Gopalan Mall', - is_bookmarked: false, - image_url: 'https://images.jdmagicbox.com/comp/chennai/s2/044pxx44.xx44.171122071536.w6s2/catalogue/v-r-mall-anna-nagar-chennai-malls-jwyyh0m5kq-t.jpg', - description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - offers_collection: [{ - name: 'Food', - offer: [{ - name: 'McDonalds Offer', - description: 'Get 25% offer on you first meal', - cupon_code: 'MCD25F' - }] - }, { - name: 'Shopping', - offer: [] - }], - rating: 4.3, - distance: 2, - location: { - latitude: 12.903903292, - longitude: 34.940349039 - } - }, - { - id: '00002', - name: 'Gopalan Mall 2', - is_bookmarked: false, - image_url: 'https://images.jdmagicbox.com/comp/chennai/s2/044pxx44.xx44.171122071536.w6s2/catalogue/v-r-mall-anna-nagar-chennai-malls-jwyyh0m5kq-t.jpg', - description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - offers_collection: [{ - name: 'Food', - offer: [] - }, { - name: 'Shopping', - offer: [] - }], - rating: 4.3, - distance: 2, - location: { - latitude: 12.903903292, - longitude: 34.940349039 - } - }, - { - id: '00003', - name: 'Gopalan Mall 3', - is_bookmarked: false, - image_url: 'https://images.jdmagicbox.com/comp/chennai/s2/044pxx44.xx44.171122071536.w6s2/catalogue/v-r-mall-anna-nagar-chennai-malls-jwyyh0m5kq-t.jpg', - description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - offers_collection: [{ - name: 'Food', - offer: [] - }, { - name: 'Shopping', - offer: [] - }], - rating: 4.3, - distance: 2, - location: { - latitude: 12.903903292, - longitude: 34.940349039 - } - }]; + private malls: Array; - constructor() { } + constructor() { + this.malls = [{ + id: '00001', + name: 'Gopalan Mall', + is_bookmarked: true, + is_archived: false, + image_url: 'https://www.gopalanmall.com/images/mall-arcade-01.jpg', + description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + outlets: [], + offer_collection: [{ + name: 'Food', + offers: [{ + name: 'McDonalds Offer', + description: 'Get 25% offer on you first meal', + coupon_code: 'MCD25F', + }] + }, { + name: 'Shopping', + offers: [] + }], + rating: 4.3, + distance: 2, + location: { + latitude: 12.903903292, + longitude: 34.940349039 + } + }, + { + id: '00002', + name: 'Gopalan Mall 2', + is_bookmarked: false, + is_archived: false, + image_url: 'https://www.gopalanmall.com/images/mall-arcade-01.jpg', + description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + outlets: [], + offer_collection: [{ + name: 'Food', + offers: [] + }, { + name: 'Shopping', + offers: [] + }], + rating: 4.3, + distance: 2, + location: { + latitude: 12.903903292, + longitude: 34.940349039 + } + }, + { + id: '00003', + name: 'Gopalan Mall 3', + is_bookmarked: false, + is_archived: false, + image_url: 'https://www.gopalanmall.com/images/mall-arcade-01.jpg', + description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + outlets: [], + offer_collection: [{ + name: 'Food', + offers: [] + }, { + name: 'Shopping', + offers: [] + }], + rating: 4.3, + distance: 2, + location: { + latitude: 12.903903292, + longitude: 34.940349039 + } + }]; + } + + public async getAllMalls() { + return this.malls; + } - getAllMalls() { - return new Promise((resolve, reject) => { - resolve(this.malls); - }); + public async getMallByID(id: string) { + return this.malls.find((mall) => mall.id === id); } } diff --git a/src/app/shared/storable.ts b/src/app/shared/storable.ts new file mode 100644 index 0000000..ee4c12c --- /dev/null +++ b/src/app/shared/storable.ts @@ -0,0 +1,8 @@ +export interface Storable { + pk?: number; + id: string; + archived: boolean; + + update: (object: object) => void; + convertToPlainObject: () => object; +} diff --git a/src/app/shared/storage-wrapper.ts b/src/app/shared/storage-wrapper.ts new file mode 100644 index 0000000..6162949 --- /dev/null +++ b/src/app/shared/storage-wrapper.ts @@ -0,0 +1,90 @@ +import { Storage } from '@ionic/storage'; +import { Storable } from './storable'; + +export class StorageWrapper { + entries: Array = []; + activeEntries: Array = []; + storageKey: string; + lastMigration = 1; + + createdIds: Set = new Set(); + updatedIds: Set = new Set(); + deletedIds: Set = new Set(); + + constructor(storageKey: string, private storageType: any, private storage: Storage, private seedData?: T[]) { + this.storageKey = storageKey; + } + + private async deserialize_entries() { + const parsedEntries = await this.storage.get(this.storageKey); + const journalEntries = parsedEntries.map(entry => new this.storageType(entry)); + return journalEntries; + } + + private async serialize_entries() { + const plainObjectEntries = this.entries.map(entry => entry.convertToPlainObject()); + return this.storage.set(this.storageKey, plainObjectEntries); + } + + async getEntries(force = false) { + if (this.entries.length === 0 || force) { + const entriesInStorage = await this.storage.get(this.storageKey); + + if (!entriesInStorage) { + this.entries = this.seedData; + await this.serialize_entries(); + } else { + this.entries = await this.deserialize_entries(); + } + + this.activeEntries = this.entries.filter(entry => !entry.archived); + } + + return this.activeEntries; + } + + async addEntry(newEntry: T) { + // Make sure the old entries have been fetched before adding new ones + await this.getEntries(); + + this.entries.push(newEntry); + this.activeEntries.push(newEntry); + + this.createdIds.add(newEntry.id); + this.serialize_entries(); + } + + async getEntryById(id: string) { + const journalEntries = await this.getEntries(); + return journalEntries.find(entry => entry.id === id); + } + + async updateEntry(updateObject: Storable) { + const entryToUpdate = this.entries.find(entry => entry.id === updateObject.id); + entryToUpdate.update(updateObject); + const newActiveEntries = this.entries.filter(entry => !entry.archived); + this.activeEntries.splice(0, Infinity, ...newActiveEntries); + + this.updatedIds.add(updateObject.id); + this.serialize_entries(); + } + + async softDeleteEntry(deletedId: string) { + this.entries.find(entry => entry.id === deletedId).archived = true; + const newActiveEntries = this.entries.filter(entry => !entry.archived); + this.activeEntries.splice(0, Infinity, ...newActiveEntries); + + this.updatedIds.add(deletedId); + this.serialize_entries(); + } + + async hardDeleteEntry(deletedId: string) { + const deletedEntryIndex = this.entries.findIndex(entry => entry.id === deletedId); + this.entries.splice(deletedEntryIndex, 1); + const deletedActiveEntryIndex = this.activeEntries.findIndex(entry => entry.id === deletedId); + this.activeEntries.splice(deletedActiveEntryIndex, Infinity); + + this.deletedIds.add(deletedId); + this.serialize_entries(); + } +} diff --git a/src/global.scss b/src/global.scss index 6e2540e..0ff3f1c 100644 --- a/src/global.scss +++ b/src/global.scss @@ -27,7 +27,7 @@ @import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap'); -@import url('https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c:400,500&display=swap'); +@import url('https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c:400&display=swap'); ion-button, button, a, p, div, input { font-family: 'Roboto', sans-serif; @@ -41,4 +41,130 @@ figure { header, h1, h2, h3, h4, h5 { font-family: 'M PLUS Rounded 1c', sans-serif; + margin: 0; +} + +// Hamburger menu + +.menu-icon-holder { + background-color: #005cbe; + color: white; + position: fixed; + bottom: -60px; + box-shadow: 0px 0px 5px var(--brand-blue); + width: 100px; + height: 100px; + text-align: center; + left: calc((100% - 100px)/2); + border-radius: 50%; + padding-top: 7px; + z-index: 1; + transition: transform 0.5s; + transform: translateY(0); + opacity: 0.7; + + &.inactive { + transform: translateY(100px); + } + + ion-icon { + font-size: 30px; + } +} + +.close-icon-holder { + background-color: white; + width: 70%; + position: fixed; + left: calc((100% - 70%) / 2); + height: 100px; + border-radius: 50%; + color: var(--brand-blue); + text-align: center; + z-index: 2; + bottom: -60px; + padding-top: 10px; + transition: transform 0.3s; + transform: scale(0); + transition-delay: 0.7s; + + &.active { + transform: scale(1); + } + + ion-icon { + font-size: 25px; + } +} + +.menu-items-holder { + background-image: url('assets/custom/background-2.svg'); + background-size: cover; + background-repeat: no-repeat; + background-position: left top; + border-radius: 50%; + position: fixed; + left: calc((100% - 150%) / 2); + bottom: -150px; + z-index: 1; + height: 300px; + width: 150%; + padding: 20px 30%; + display: flex; + justify-content: space-between; + align-items: flex-start; + transition: transform 0.5s; + transform: scale(0); + transition-delay: 0.3s; + + &.active { + transform: scale(1); + } + + button { + background-color: white; + height: 50px; + width: 50px; + border-radius: 50%; + font-size: 20px; + position: relative; + + &:first-child { + transform: translateY(40px); + } + + &:nth-child(2) { + transform: translateY(20px); + } + + &:nth-child(3) { + transform: translateY(10px); + } + + &:last-child { + transform: translateY(40px); + } + + &:nth-child(4) { + transform: translateY(20px); + } + } +} + +.overlay { + position: fixed; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + pointer-events: none; + background-color: black; + opacity: 0; + z-index: 1; + transition: opacity 0.5s; + + &.active { + pointer-events: all; + opacity: 0.5; + } } diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 9b1730f..24d2ba9 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -75,7 +75,7 @@ --ion-color-light-shade: #d7d8da; --ion-color-light-tint: #f5f6f9; - --brand-blue: #8173e6; + --brand-blue: #1881e5; --background-blue: #f5f7fa; --brand-grey: #9a9a9a; --brand-black: #1b1d1e;
{{ mall_details.description }}
{{ mall.description }}