22 Коммиты

Автор SHA1 Сообщение Дата
  eliam.ruiz 4a226b4199 Ran app in android studio in order to verify that it worked 2 лет назад
  eliam.ruiz 13edd3582f Merge branch 'main' of https://git.ccom.uprrp.edu/sergio.mattei/RenacerSocial into eliam-sprint-1 2 лет назад
  eliam.ruiz c4e6fad61f Add my cypress testing functions 2 лет назад
  Sergio Mattei 27ad2ea2af [fix] Fix CORS issue 2 лет назад
  Sergio Mattei 46b3710a27 [feat] Add cypress command 2 лет назад
  Sergio Mattei 5a7212624d [feat] Add E2E testing 2 лет назад
  Sergio Mattei f93263078e [feat] Add multiple categories 2 лет назад
  Sergio Mattei d94c95d5a4 Merge branch 'main' into matteing-sprint-1 2 лет назад
  eliam.ruiz 63c50d8956 Merge 2 лет назад
  Sergio Mattei d3cfb9af94 Merge branch 'main' into matteing-sprint-1 2 лет назад
  Sergio Mattei f73408f895 [feat] Don't use dropdown 2 лет назад
  eliam.ruiz 0ef5cb714a Merge branch 'eduardo-sprint-2' 2 лет назад
  eduardo.figueroa7 775ec9a0bb editar paginas de servicios 2 лет назад
  sergio.mattei 2213cd9df8 Merge branch 'matteing-sprint-1' of sergio.mattei/RenacerSocial into main 2 лет назад
  Sergio Mattei b2e0660ba3 [mant] Merge all current conflicta 2 лет назад
  Sergio Mattei ed9ba1e37f Merge branch 'main' into matteing-sprint-1 2 лет назад
  Sergio Mattei 9fda4b116b [feat] Add routing for Notion articles 2 лет назад
  eduardo.figueroa7 61122a8031 Crear el about y editar logos 2 лет назад
  eduardo.figueroa7 1712cae972 Crear el about y editar logos 2 лет назад
  Sergio Mattei 9ac7df12d6 [feat] Refinements to UX for fetching data from Notion 2 лет назад
  Sergio Mattei ba67862d80 [feat] Add proof of concept Notion integration 2 лет назад
  eliam.ruiz a92ce928f8 Merge branch 'eliam-sprint-1' of sergio.mattei/RenacerSocial into main 2 лет назад
50 измененных файлов: 5718 добавлений и 166 удалений
  1. 15
    0
      .expo/README.md
  2. 8
    0
      .expo/settings.json
  3. 10
    0
      android/app/src/main/res/xml/config2.xml
  4. 5
    5
      capacitor.config.ts
  5. 10
    0
      cypress.config.ts
  6. 2
    0
      cypress/e2e/eduardo.cy.ts
  7. 50
    0
      cypress/e2e/eliam.cy.ts
  8. 56
    0
      cypress/e2e/sergio.cy.ts
  9. 1643
    0
      cypress/fixtures/article.json
  10. 14
    0
      cypress/fixtures/topics.json
  11. 37
    0
      cypress/support/commands.ts
  12. 20
    0
      cypress/support/e2e.ts
  13. 6
    2
      ios/App/App.xcodeproj/project.pbxproj
  14. 1
    1
      ios/App/App/Info.plist
  15. 10
    0
      ios/App/App/config 2.xml
  16. 10
    0
      ios/App/App/config 3.xml
  17. 1
    0
      ios/App/Podfile
  18. 52
    0
      ios/App/Podfile 2.lock
  19. 52
    0
      ios/App/Podfile 3.lock
  20. 52
    0
      ios/App/Podfile 4.lock
  21. 3038
    18
      package-lock.json
  22. 14
    2
      package.json
  23. Двоичные данные
      public/assets/icon/about.png
  24. Двоичные данные
      public/assets/icon/articule.png
  25. Двоичные данные
      public/assets/icon/email.png
  26. Двоичные данные
      public/assets/icon/law.jpeg
  27. Двоичные данные
      public/assets/icon/telephone.png
  28. 49
    4
      src/App.tsx
  29. 30
    0
      src/components/ButtonComponent.tsx
  30. 0
    3
      src/components/DropdownComponent.css
  31. 0
    34
      src/components/DropdownComponent.tsx
  32. 9
    6
      src/components/ListComponent.css
  33. 0
    27
      src/components/ListComponent.tsx
  34. 12
    0
      src/components/SkeletonText.tsx
  35. 13
    0
      src/components/TopicList.css
  36. 61
    0
      src/components/TopicList.tsx
  37. 1
    0
      src/config.ts
  38. 4
    0
      src/lib/api.ts
  39. 72
    0
      src/pages/AboutListPage.tsx
  40. 45
    17
      src/pages/AdviceListPage.tsx
  41. 48
    17
      src/pages/ArticlePage.tsx
  42. 15
    8
      src/pages/Home.tsx
  43. 20
    2
      src/pages/LawListPage.tsx
  44. 46
    0
      src/pages/services/CasitaPage.tsx
  45. 46
    0
      src/pages/services/CrecemosPage.tsx
  46. 46
    0
      src/pages/services/LazosPage.tsx
  47. 48
    0
      src/pages/services/SupervisadasPage.tsx
  48. 37
    0
      src/theme/global.css
  49. 2
    16
      src/theme/variables.css
  50. 8
    4
      src/types.ts

+ 15
- 0
.expo/README.md Просмотреть файл

1
+> Why do I have a folder named ".expo" in my project?
2
+
3
+The ".expo" folder is created when an Expo project is started using "expo start" command.
4
+
5
+> What do the files contain?
6
+
7
+- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
8
+- "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator.
9
+- "settings.json": contains the server configuration that is used to serve the application manifest.
10
+
11
+> Should I commit the ".expo" folder?
12
+
13
+No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
14
+
15
+Upon project creation, the ".expo" folder is already added to your ".gitignore" file.

+ 8
- 0
.expo/settings.json Просмотреть файл

1
+{
2
+  "hostType": "lan",
3
+  "lanType": "ip",
4
+  "dev": true,
5
+  "minify": false,
6
+  "urlRandomness": null,
7
+  "https": false
8
+}

+ 10
- 0
android/app/src/main/res/xml/config2.xml Просмотреть файл

1
+<?xml version='1.0' encoding='utf-8'?>
2
+<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3
+  <access origin="*" />
4
+  
5
+  <feature name="CallNumber">
6
+    <param name="android-package" value="mx.ferreyra.callnumber.CFCallNumber"/>
7
+  </feature>
8
+
9
+  
10
+</widget>

+ 5
- 5
capacitor.config.ts Просмотреть файл

1
-import { CapacitorConfig } from '@capacitor/cli';
1
+import { CapacitorConfig } from "@capacitor/cli";
2
 
2
 
3
 const config: CapacitorConfig = {
3
 const config: CapacitorConfig = {
4
-  appId: 'io.ionic.starter',
5
-  appName: 'renacer',
6
-  webDir: 'build',
7
-  bundledWebRuntime: false
4
+  appId: "io.renacer.social",
5
+  appName: "renacer",
6
+  webDir: "build",
7
+  bundledWebRuntime: false,
8
 };
8
 };
9
 
9
 
10
 export default config;
10
 export default config;

+ 10
- 0
cypress.config.ts Просмотреть файл

1
+import { defineConfig } from "cypress";
2
+
3
+export default defineConfig({
4
+  e2e: {
5
+    baseUrl: "http://localhost:3001",
6
+    setupNodeEvents(on, config) {
7
+      // implement node event listeners here
8
+    },
9
+  },
10
+});

+ 2
- 0
cypress/e2e/eduardo.cy.ts Просмотреть файл

1
+// I will E2E test the article page.
2
+// User visits home -> User clicks on "laws or advice" -> List renders with 200

+ 50
- 0
cypress/e2e/eliam.cy.ts Просмотреть файл

1
+// I will E2E test the article page.
2
+// User visits home -> User clicks on "laws or advice" -> Article page contains the title
3
+
4
+
5
+describe("Our app", () => {
6
+    beforeEach(() => {
7
+      cy.intercept(
8
+        { pathname: "/api/getTopicList" },
9
+        {
10
+          fixture: "topics",
11
+        }
12
+      ).as("getTopicList");
13
+  
14
+      cy.intercept(
15
+        { pathname: "/api/getTopic" },
16
+        {
17
+          fixture: "article",
18
+        }
19
+      ).as("getTopic");
20
+  
21
+      cy.viewport("iphone-x");
22
+      cy.visit("/");
23
+    });
24
+
25
+    it("shows the home page", () => {
26
+        cy.visit("/");
27
+        cy.contains("Bienvenidos");
28
+    });
29
+
30
+    it("clicks and loads the advice tab", () => {
31
+        cy.get("#tab-button-advice").first().click();
32
+        cy.wait("@getTopicList");
33
+    })
34
+    it("clicks and loads advice info", () => {
35
+        cy.get("#tab-button-advice").first().click();
36
+        cy.wait("@getTopicList");
37
+        cy.get(".item").first().click();
38
+        cy.wait("@getTopic");
39
+        cy.contains("Adopción");
40
+        cy.contains("Enmendado en el 1939");
41
+    })
42
+    it("clicks and loads law info", () => {
43
+        cy.get("#tab-button-laws").first().click();
44
+        cy.wait("@getTopicList");
45
+        cy.get(".item").first().click();
46
+        cy.wait("@getTopic");
47
+        cy.contains("Adopción");
48
+        cy.contains("Enmendado en el 1939");
49
+    })
50
+})

+ 56
- 0
cypress/e2e/sergio.cy.ts Просмотреть файл

1
+describe("Our app", () => {
2
+  beforeEach(() => {
3
+    cy.intercept(
4
+      { pathname: "/api/getTopicList" },
5
+      {
6
+        fixture: "topics",
7
+      }
8
+    ).as("getTopicList");
9
+
10
+    cy.intercept(
11
+      { pathname: "/api/getTopic" },
12
+      {
13
+        fixture: "article",
14
+      }
15
+    ).as("getTopic");
16
+
17
+    cy.viewport("iphone-x");
18
+    cy.visit("/");
19
+  });
20
+
21
+  it("shows the home page", () => {
22
+    cy.visit("/");
23
+    cy.contains("Bienvenidos");
24
+  });
25
+
26
+  it("shows a video in the home page", () => {
27
+    cy.visit("/");
28
+    cy.get("iframe").should("be.visible");
29
+  });
30
+
31
+  it("shows the about page", () => {
32
+    cy.visit("/about");
33
+    cy.contains("Renacer Social");
34
+  });
35
+
36
+  it("shows the individual service pages", () => {
37
+    cy.visit("/about");
38
+    cy.get(".item").first().click();
39
+    cy.contains("Casita de Paz");
40
+  });
41
+
42
+  it("shows the advice pages", () => {
43
+    cy.visit("/advice");
44
+    // Wait until the request to the intercept is completed.
45
+    cy.wait("@getTopicList");
46
+    cy.get(".item").first().click();
47
+    cy.wait("@getTopic");
48
+  });
49
+
50
+  it("allows exiting the individual service pages", () => {
51
+    cy.visit("/about");
52
+    cy.get(".item").first().click();
53
+    cy.get(".back-button-has-icon-only").first().click();
54
+    cy.contains("Servicios");
55
+  });
56
+});

+ 1643
- 0
cypress/fixtures/article.json
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


+ 14
- 0
cypress/fixtures/topics.json Просмотреть файл

1
+[
2
+  {
3
+    "blockId": "07ffbf26-babe-4c6a-b046-c7eb6d5c1a7f",
4
+    "name": "Pasa tiempo con tus hijos"
5
+  },
6
+  {
7
+    "blockId": "754d8ef3-14ae-4af6-a8c3-4ac4ede4d49d",
8
+    "name": "10 consejos para un matrimonio feliz"
9
+  },
10
+  {
11
+    "blockId": "d9fbaf7c-8f89-43b3-945c-927ed90e098b",
12
+    "name": "Busca ayuda si eres abusado/abusada"
13
+  }
14
+]

+ 37
- 0
cypress/support/commands.ts Просмотреть файл

1
+/// <reference types="cypress" />
2
+// ***********************************************
3
+// This example commands.ts shows you how to
4
+// create various custom commands and overwrite
5
+// existing commands.
6
+//
7
+// For more comprehensive examples of custom
8
+// commands please read more here:
9
+// https://on.cypress.io/custom-commands
10
+// ***********************************************
11
+//
12
+//
13
+// -- This is a parent command --
14
+// Cypress.Commands.add('login', (email, password) => { ... })
15
+//
16
+//
17
+// -- This is a child command --
18
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
19
+//
20
+//
21
+// -- This is a dual command --
22
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
23
+//
24
+//
25
+// -- This will overwrite an existing command --
26
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
27
+//
28
+// declare global {
29
+//   namespace Cypress {
30
+//     interface Chainable {
31
+//       login(email: string, password: string): Chainable<void>
32
+//       drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
33
+//       dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
34
+//       visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
35
+//     }
36
+//   }
37
+// }

+ 20
- 0
cypress/support/e2e.ts Просмотреть файл

1
+// ***********************************************************
2
+// This example support/e2e.ts is processed and
3
+// loaded automatically before your test files.
4
+//
5
+// This is a great place to put global configuration and
6
+// behavior that modifies Cypress.
7
+//
8
+// You can change the location of this file or turn off
9
+// automatically serving support files with the
10
+// 'supportFile' configuration option.
11
+//
12
+// You can read more here:
13
+// https://on.cypress.io/configuration
14
+// ***********************************************************
15
+
16
+// Import commands.js using ES2015 syntax:
17
+import "./commands";
18
+
19
+// Alternatively you can use CommonJS syntax:
20
+// require('./commands')

+ 6
- 2
ios/App/App.xcodeproj/project.pbxproj Просмотреть файл

347
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
347
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
348
 				CODE_SIGN_STYLE = Automatic;
348
 				CODE_SIGN_STYLE = Automatic;
349
 				CURRENT_PROJECT_VERSION = 1;
349
 				CURRENT_PROJECT_VERSION = 1;
350
+				DEVELOPMENT_TEAM = N9M43N6FY5;
350
 				INFOPLIST_FILE = App/Info.plist;
351
 				INFOPLIST_FILE = App/Info.plist;
352
+				INFOPLIST_KEY_CFBundleDisplayName = "Renacer Social";
351
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
353
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
352
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
354
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
353
 				MARKETING_VERSION = 1.0;
355
 				MARKETING_VERSION = 1.0;
354
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
356
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
355
-				PRODUCT_BUNDLE_IDENTIFIER = io.ionic.starter;
357
+				PRODUCT_BUNDLE_IDENTIFIER = io.renacer.social;
356
 				PRODUCT_NAME = "$(TARGET_NAME)";
358
 				PRODUCT_NAME = "$(TARGET_NAME)";
357
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
359
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
358
 				SWIFT_VERSION = 5.0;
360
 				SWIFT_VERSION = 5.0;
367
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
369
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
368
 				CODE_SIGN_STYLE = Automatic;
370
 				CODE_SIGN_STYLE = Automatic;
369
 				CURRENT_PROJECT_VERSION = 1;
371
 				CURRENT_PROJECT_VERSION = 1;
372
+				DEVELOPMENT_TEAM = N9M43N6FY5;
370
 				INFOPLIST_FILE = App/Info.plist;
373
 				INFOPLIST_FILE = App/Info.plist;
374
+				INFOPLIST_KEY_CFBundleDisplayName = "Renacer Social";
371
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
375
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
372
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
376
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
373
 				MARKETING_VERSION = 1.0;
377
 				MARKETING_VERSION = 1.0;
374
-				PRODUCT_BUNDLE_IDENTIFIER = io.ionic.starter;
378
+				PRODUCT_BUNDLE_IDENTIFIER = io.renacer.social;
375
 				PRODUCT_NAME = "$(TARGET_NAME)";
379
 				PRODUCT_NAME = "$(TARGET_NAME)";
376
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
380
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
377
 				SWIFT_VERSION = 5.0;
381
 				SWIFT_VERSION = 5.0;

+ 1
- 1
ios/App/App/Info.plist Просмотреть файл

5
 	<key>CFBundleDevelopmentRegion</key>
5
 	<key>CFBundleDevelopmentRegion</key>
6
 	<string>en</string>
6
 	<string>en</string>
7
 	<key>CFBundleDisplayName</key>
7
 	<key>CFBundleDisplayName</key>
8
-        <string>renacer</string>
8
+	<string>renacer</string>
9
 	<key>CFBundleExecutable</key>
9
 	<key>CFBundleExecutable</key>
10
 	<string>$(EXECUTABLE_NAME)</string>
10
 	<string>$(EXECUTABLE_NAME)</string>
11
 	<key>CFBundleIdentifier</key>
11
 	<key>CFBundleIdentifier</key>

+ 10
- 0
ios/App/App/config 2.xml Просмотреть файл

1
+<?xml version='1.0' encoding='utf-8'?>
2
+<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3
+  <access origin="*" />
4
+  
5
+  <feature name="CallNumber">
6
+    <param name="ios-package" value="CFCallNumber"/>
7
+  </feature>
8
+
9
+  
10
+</widget>

+ 10
- 0
ios/App/App/config 3.xml Просмотреть файл

1
+<?xml version='1.0' encoding='utf-8'?>
2
+<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3
+  <access origin="*" />
4
+  
5
+  <feature name="CallNumber">
6
+    <param name="ios-package" value="CFCallNumber"/>
7
+  </feature>
8
+
9
+  
10
+</widget>

+ 1
- 0
ios/App/Podfile Просмотреть файл

15
   pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
15
   pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
16
   pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
16
   pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
17
   pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
17
   pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
18
+  pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
18
 end
19
 end
19
 
20
 
20
 target 'App' do
21
 target 'App' do

+ 52
- 0
ios/App/Podfile 2.lock Просмотреть файл

1
+PODS:
2
+  - Capacitor (4.4.0):
3
+    - CapacitorCordova
4
+  - CapacitorApp (4.1.0):
5
+    - Capacitor
6
+  - CapacitorCordova (4.4.0)
7
+  - CapacitorHaptics (4.0.1):
8
+    - Capacitor
9
+  - CapacitorKeyboard (4.0.1):
10
+    - Capacitor
11
+  - CapacitorStatusBar (4.0.1):
12
+    - Capacitor
13
+  - CordovaPlugins (4.4.0):
14
+    - CapacitorCordova
15
+
16
+DEPENDENCIES:
17
+  - "Capacitor (from `../../node_modules/@capacitor/ios`)"
18
+  - "CapacitorApp (from `../../node_modules/@capacitor/app`)"
19
+  - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
20
+  - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
21
+  - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
22
+  - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
23
+  - CordovaPlugins (from `../capacitor-cordova-ios-plugins`)
24
+
25
+EXTERNAL SOURCES:
26
+  Capacitor:
27
+    :path: "../../node_modules/@capacitor/ios"
28
+  CapacitorApp:
29
+    :path: "../../node_modules/@capacitor/app"
30
+  CapacitorCordova:
31
+    :path: "../../node_modules/@capacitor/ios"
32
+  CapacitorHaptics:
33
+    :path: "../../node_modules/@capacitor/haptics"
34
+  CapacitorKeyboard:
35
+    :path: "../../node_modules/@capacitor/keyboard"
36
+  CapacitorStatusBar:
37
+    :path: "../../node_modules/@capacitor/status-bar"
38
+  CordovaPlugins:
39
+    :path: "../capacitor-cordova-ios-plugins"
40
+
41
+SPEC CHECKSUMS:
42
+  Capacitor: 868367fcfeb3ba6bfabaefc63c072e6478bd046d
43
+  CapacitorApp: 6e250f9e67560a340aac4078af357fb5e5c303e0
44
+  CapacitorCordova: 0c0b4edc7ce94fd560eadc3a6d36d5ee6453ada6
45
+  CapacitorHaptics: 02d48e2efca06ccf3273dbb638b68b0e145f623a
46
+  CapacitorKeyboard: 3b49a9d517641c123fb691713f99a89d6ebf3a37
47
+  CapacitorStatusBar: e6cf2a9ed07f92ddae9ca101be36d08c61866b35
48
+  CordovaPlugins: c646c530b24da2d898e32369a739e7b88904b548
49
+
50
+PODFILE CHECKSUM: 0381d09167db361ed446a10f049fd27bab1b40b1
51
+
52
+COCOAPODS: 1.11.3

+ 52
- 0
ios/App/Podfile 3.lock Просмотреть файл

1
+PODS:
2
+  - Capacitor (4.4.0):
3
+    - CapacitorCordova
4
+  - CapacitorApp (4.1.0):
5
+    - Capacitor
6
+  - CapacitorCordova (4.4.0)
7
+  - CapacitorHaptics (4.0.1):
8
+    - Capacitor
9
+  - CapacitorKeyboard (4.0.1):
10
+    - Capacitor
11
+  - CapacitorStatusBar (4.0.1):
12
+    - Capacitor
13
+  - CordovaPlugins (4.4.0):
14
+    - CapacitorCordova
15
+
16
+DEPENDENCIES:
17
+  - "Capacitor (from `../../node_modules/@capacitor/ios`)"
18
+  - "CapacitorApp (from `../../node_modules/@capacitor/app`)"
19
+  - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
20
+  - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
21
+  - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
22
+  - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
23
+  - CordovaPlugins (from `../capacitor-cordova-ios-plugins`)
24
+
25
+EXTERNAL SOURCES:
26
+  Capacitor:
27
+    :path: "../../node_modules/@capacitor/ios"
28
+  CapacitorApp:
29
+    :path: "../../node_modules/@capacitor/app"
30
+  CapacitorCordova:
31
+    :path: "../../node_modules/@capacitor/ios"
32
+  CapacitorHaptics:
33
+    :path: "../../node_modules/@capacitor/haptics"
34
+  CapacitorKeyboard:
35
+    :path: "../../node_modules/@capacitor/keyboard"
36
+  CapacitorStatusBar:
37
+    :path: "../../node_modules/@capacitor/status-bar"
38
+  CordovaPlugins:
39
+    :path: "../capacitor-cordova-ios-plugins"
40
+
41
+SPEC CHECKSUMS:
42
+  Capacitor: 868367fcfeb3ba6bfabaefc63c072e6478bd046d
43
+  CapacitorApp: 6e250f9e67560a340aac4078af357fb5e5c303e0
44
+  CapacitorCordova: 0c0b4edc7ce94fd560eadc3a6d36d5ee6453ada6
45
+  CapacitorHaptics: 02d48e2efca06ccf3273dbb638b68b0e145f623a
46
+  CapacitorKeyboard: 3b49a9d517641c123fb691713f99a89d6ebf3a37
47
+  CapacitorStatusBar: e6cf2a9ed07f92ddae9ca101be36d08c61866b35
48
+  CordovaPlugins: c646c530b24da2d898e32369a739e7b88904b548
49
+
50
+PODFILE CHECKSUM: 0381d09167db361ed446a10f049fd27bab1b40b1
51
+
52
+COCOAPODS: 1.11.3

+ 52
- 0
ios/App/Podfile 4.lock Просмотреть файл

1
+PODS:
2
+  - Capacitor (4.4.0):
3
+    - CapacitorCordova
4
+  - CapacitorApp (4.1.0):
5
+    - Capacitor
6
+  - CapacitorCordova (4.4.0)
7
+  - CapacitorHaptics (4.0.1):
8
+    - Capacitor
9
+  - CapacitorKeyboard (4.0.1):
10
+    - Capacitor
11
+  - CapacitorStatusBar (4.0.1):
12
+    - Capacitor
13
+  - CordovaPlugins (4.4.0):
14
+    - CapacitorCordova
15
+
16
+DEPENDENCIES:
17
+  - "Capacitor (from `../../node_modules/@capacitor/ios`)"
18
+  - "CapacitorApp (from `../../node_modules/@capacitor/app`)"
19
+  - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
20
+  - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
21
+  - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
22
+  - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
23
+  - CordovaPlugins (from `../capacitor-cordova-ios-plugins`)
24
+
25
+EXTERNAL SOURCES:
26
+  Capacitor:
27
+    :path: "../../node_modules/@capacitor/ios"
28
+  CapacitorApp:
29
+    :path: "../../node_modules/@capacitor/app"
30
+  CapacitorCordova:
31
+    :path: "../../node_modules/@capacitor/ios"
32
+  CapacitorHaptics:
33
+    :path: "../../node_modules/@capacitor/haptics"
34
+  CapacitorKeyboard:
35
+    :path: "../../node_modules/@capacitor/keyboard"
36
+  CapacitorStatusBar:
37
+    :path: "../../node_modules/@capacitor/status-bar"
38
+  CordovaPlugins:
39
+    :path: "../capacitor-cordova-ios-plugins"
40
+
41
+SPEC CHECKSUMS:
42
+  Capacitor: 868367fcfeb3ba6bfabaefc63c072e6478bd046d
43
+  CapacitorApp: 6e250f9e67560a340aac4078af357fb5e5c303e0
44
+  CapacitorCordova: 0c0b4edc7ce94fd560eadc3a6d36d5ee6453ada6
45
+  CapacitorHaptics: 02d48e2efca06ccf3273dbb638b68b0e145f623a
46
+  CapacitorKeyboard: 3b49a9d517641c123fb691713f99a89d6ebf3a37
47
+  CapacitorStatusBar: e6cf2a9ed07f92ddae9ca101be36d08c61866b35
48
+  CordovaPlugins: c646c530b24da2d898e32369a739e7b88904b548
49
+
50
+PODFILE CHECKSUM: 0381d09167db361ed446a10f049fd27bab1b40b1
51
+
52
+COCOAPODS: 1.11.3

+ 3038
- 18
package-lock.json
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


+ 14
- 2
package.json Просмотреть файл

3
   "version": "0.0.1",
3
   "version": "0.0.1",
4
   "private": true,
4
   "private": true,
5
   "dependencies": {
5
   "dependencies": {
6
+    "@awesome-cordova-plugins/call-number": "^6.2.0",
7
+    "@awesome-cordova-plugins/core": "^6.2.0",
8
+    "@awesome-cordova-plugins/email-composer": "^6.2.0",
6
     "@capacitor/android": "4.4.0",
9
     "@capacitor/android": "4.4.0",
7
     "@capacitor/app": "^4.1.0",
10
     "@capacitor/app": "^4.1.0",
8
     "@capacitor/core": "4.4.0",
11
     "@capacitor/core": "4.4.0",
10
     "@capacitor/ios": "4.4.0",
13
     "@capacitor/ios": "4.4.0",
11
     "@capacitor/keyboard": "^4.0.1",
14
     "@capacitor/keyboard": "^4.0.1",
12
     "@capacitor/status-bar": "^4.0.1",
15
     "@capacitor/status-bar": "^4.0.1",
16
+    "@ionic-native/call-number": "^5.36.0",
13
     "@ionic/react": "^6.0.0",
17
     "@ionic/react": "^6.0.0",
14
     "@ionic/react-router": "^6.0.0",
18
     "@ionic/react-router": "^6.0.0",
15
     "@testing-library/jest-dom": "^5.11.9",
19
     "@testing-library/jest-dom": "^5.11.9",
21
     "@types/react-dom": "^18.0.6",
25
     "@types/react-dom": "^18.0.6",
22
     "@types/react-router": "^5.1.11",
26
     "@types/react-router": "^5.1.11",
23
     "@types/react-router-dom": "^5.1.7",
27
     "@types/react-router-dom": "^5.1.7",
28
+    "call-number": "^1.0.1",
24
     "history": "^4.9.0",
29
     "history": "^4.9.0",
25
     "ionicons": "^6.0.3",
30
     "ionicons": "^6.0.3",
31
+    "lodash": "^4.17.21",
32
+    "notion-utils": "^6.15.6",
26
     "react": "^18.2.0",
33
     "react": "^18.2.0",
27
     "react-dom": "^18.2.0",
34
     "react-dom": "^18.2.0",
35
+    "react-notion-x": "^6.15.7",
28
     "react-router": "^5.2.0",
36
     "react-router": "^5.2.0",
29
     "react-router-dom": "^5.2.0",
37
     "react-router-dom": "^5.2.0",
30
     "react-scripts": "^5.0.0",
38
     "react-scripts": "^5.0.0",
39
+    "swr": "^1.3.0",
31
     "typescript": "^4.1.3",
40
     "typescript": "^4.1.3",
32
     "web-vitals": "^0.2.4",
41
     "web-vitals": "^0.2.4",
33
     "workbox-background-sync": "^5.1.4",
42
     "workbox-background-sync": "^5.1.4",
44
     "workbox-streams": "^5.1.4"
53
     "workbox-streams": "^5.1.4"
45
   },
54
   },
46
   "scripts": {
55
   "scripts": {
47
-    "start": "react-scripts start",
56
+    "start": "PORT=3001 react-scripts start",
48
     "build": "react-scripts build",
57
     "build": "react-scripts build",
49
     "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
58
     "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
50
     "eject": "react-scripts eject",
59
     "eject": "react-scripts eject",
51
-    "prepare": "husky install"
60
+    "prepare": "husky install",
61
+    "cypress": "cypress open"
52
   },
62
   },
53
   "eslintConfig": {
63
   "eslintConfig": {
54
     "extends": [
64
     "extends": [
71
   "devDependencies": {
81
   "devDependencies": {
72
     "@capacitor/cli": "4.4.0",
82
     "@capacitor/cli": "4.4.0",
73
     "@ionic/lab": "3.2.15",
83
     "@ionic/lab": "3.2.15",
84
+    "@types/lodash": "^4.14.191",
85
+    "cypress": "^12.1.0",
74
     "husky": "^8.0.0",
86
     "husky": "^8.0.0",
75
     "pretty-quick": "^3.1.3"
87
     "pretty-quick": "^3.1.3"
76
   },
88
   },

Двоичные данные
public/assets/icon/about.png Просмотреть файл


Двоичные данные
public/assets/icon/articule.png Просмотреть файл


Двоичные данные
public/assets/icon/email.png Просмотреть файл


Двоичные данные
public/assets/icon/law.jpeg Просмотреть файл


Двоичные данные
public/assets/icon/telephone.png Просмотреть файл


+ 49
- 4
src/App.tsx Просмотреть файл

10
   setupIonicReact,
10
   setupIonicReact,
11
 } from "@ionic/react";
11
 } from "@ionic/react";
12
 import { IonReactRouter } from "@ionic/react-router";
12
 import { IonReactRouter } from "@ionic/react-router";
13
-import { ellipse, home } from "ionicons/icons";
13
+import {
14
+  ellipse,
15
+  informationCircleSharp,
16
+  bookSharp,
17
+  appsSharp,
18
+  home,
19
+  peopleCircleSharp,
20
+} from "ionicons/icons";
14
 import Home from "./pages/Home";
21
 import Home from "./pages/Home";
15
 
22
 
16
 /* Core CSS required for Ionic components to work properly */
23
 /* Core CSS required for Ionic components to work properly */
34
 import AdviceListPage from "./pages/AdviceListPage";
41
 import AdviceListPage from "./pages/AdviceListPage";
35
 import LawListPage from "./pages/LawListPage";
42
 import LawListPage from "./pages/LawListPage";
36
 import ArticlePage from "./pages/ArticlePage";
43
 import ArticlePage from "./pages/ArticlePage";
44
+import "./theme/global.css";
45
+import "react-notion-x/src/styles.css";
46
+import AboutListPage from "./pages/AboutListPage";
47
+import CasitaPage from "./pages/services/CasitaPage";
48
+import CrecemosPage from "./pages/services/CrecemosPage";
49
+import LazosPage from "./pages/services/LazosPage";
50
+import SupervisadasPage from "./pages/services/SupervisadasPage";
37
 
51
 
38
 setupIonicReact();
52
 setupIonicReact();
39
 
53
 
45
           <Route exact path="/home">
59
           <Route exact path="/home">
46
             <Home />
60
             <Home />
47
           </Route>
61
           </Route>
62
+
48
           <Route path="/advice">
63
           <Route path="/advice">
49
             <AdviceListPage />
64
             <AdviceListPage />
50
           </Route>
65
           </Route>
66
+
51
           <Route path="/laws">
67
           <Route path="/laws">
52
             <LawListPage />
68
             <LawListPage />
53
           </Route>
69
           </Route>
54
-          <Route path="/article/:slug" component={ArticlePage} />
70
+
71
+          <Route path="/article/:articleId" component={ArticlePage} />
72
+
73
+          <Route path="/services/lazos">
74
+            <LazosPage />
75
+          </Route>
76
+
77
+          <Route path="/services/crecemos">
78
+            <CrecemosPage />
79
+          </Route>
80
+
81
+          <Route path="/services/supervisadas">
82
+            <SupervisadasPage />
83
+          </Route>
84
+
85
+          <Route path="/about">
86
+            <AboutListPage />
87
+          </Route>
88
+
89
+          <Route path="/services/casita">
90
+            <CasitaPage />
91
+          </Route>
92
+
55
           <Route exact path="/">
93
           <Route exact path="/">
56
             <Redirect to="/home" />
94
             <Redirect to="/home" />
57
           </Route>
95
           </Route>
58
         </IonRouterOutlet>
96
         </IonRouterOutlet>
97
+
59
         <IonTabBar slot="bottom">
98
         <IonTabBar slot="bottom">
60
           <IonTabButton tab="home" href="/home">
99
           <IonTabButton tab="home" href="/home">
61
             <IonIcon icon={home} />
100
             <IonIcon icon={home} />
62
             <IonLabel>Home</IonLabel>
101
             <IonLabel>Home</IonLabel>
63
           </IonTabButton>
102
           </IonTabButton>
103
+
64
           <IonTabButton tab="advice" href="/advice">
104
           <IonTabButton tab="advice" href="/advice">
65
-            <IonIcon icon={ellipse} />
105
+            <IonIcon icon={peopleCircleSharp} />
66
             <IonLabel>Advice</IonLabel>
106
             <IonLabel>Advice</IonLabel>
67
           </IonTabButton>
107
           </IonTabButton>
108
+
68
           <IonTabButton tab="laws" href="/laws">
109
           <IonTabButton tab="laws" href="/laws">
69
-            <IonIcon icon={ellipse} />
110
+            <IonIcon icon={bookSharp} />
70
             <IonLabel>Laws</IonLabel>
111
             <IonLabel>Laws</IonLabel>
71
           </IonTabButton>
112
           </IonTabButton>
113
+          <IonTabButton tab="about" href="/about">
114
+            <IonIcon icon={informationCircleSharp} />
115
+            <IonLabel>About</IonLabel>
116
+          </IonTabButton>
72
         </IonTabBar>
117
         </IonTabBar>
73
       </IonTabs>
118
       </IonTabs>
74
     </IonReactRouter>
119
     </IonReactRouter>

+ 30
- 0
src/components/ButtonComponent.tsx Просмотреть файл

1
+import { IonButton, IonContent, IonIcon, IonItem, IonText } from "@ionic/react";
2
+import { CallNumber } from "@awesome-cordova-plugins/call-number";
3
+import {
4
+ call,
5
+ mailOpen
6
+  } from "ionicons/icons";
7
+
8
+const ButtonComponent: React.FC<{
9
+  body: string;
10
+}> = (props) => {
11
+  const body = props.body;
12
+  return (
13
+    <IonText>
14
+      <IonButton expand="block" 
15
+        onClick={() => {
16
+          CallNumber.callNumber("7876247846", true);
17
+          alert("testing is this works");
18
+        }}
19
+      >
20
+         <IonIcon slot="start" icon={call}> </IonIcon>
21
+        Llamada
22
+      </IonButton>
23
+      <a href={"mailto:eliamruiz2027@gmail.com?cc=eliam.ruiz@upr.edu,&subject=Testing Ionic button&body=" + body + ""}>
24
+        <IonButton expand="block">
25
+        <IonIcon slot="start" icon={mailOpen}> </IonIcon> Correo Electrónico</IonButton>
26
+      </a>
27
+    </IonText>
28
+  );
29
+};
30
+export default ButtonComponent;

+ 0
- 3
src/components/DropdownComponent.css Просмотреть файл

1
-.accordion-list-container {
2
-    width: 100%;
3
-}

+ 0
- 34
src/components/DropdownComponent.tsx Просмотреть файл

1
-import {
2
-  IonAccordion,
3
-  IonAccordionGroup,
4
-  IonItem,
5
-  IonLabel,
6
-} from "@ionic/react";
7
-import ListComponent from "../components/ListComponent";
8
-import { Category } from "../types";
9
-import "./DropdownComponent.css";
10
-
11
-const DropdownComponent: React.FC<{
12
-  category: Category[];
13
-}> = (props) => {
14
-  const category = props.category;
15
-  return (
16
-    <IonAccordionGroup>
17
-      {category.map((catItem: Category, index) => {
18
-        return (
19
-          <IonAccordion value={`${index}`}>
20
-            <IonItem slot="header" color="light">
21
-              <IonLabel>{catItem.name}</IonLabel>
22
-            </IonItem>
23
-            <IonItem slot="content">
24
-              <div className="accordion-list-container">
25
-                <ListComponent items={catItem.listItems} />
26
-              </div>
27
-            </IonItem>
28
-          </IonAccordion>
29
-        );
30
-      })}
31
-    </IonAccordionGroup>
32
-  );
33
-};
34
-export default DropdownComponent;

+ 9
- 6
src/components/ListComponent.css Просмотреть файл

1
-
2
 .list {
1
 .list {
3
-    border-radius: 20px; 
4
-    padding: 0 0 0 0;
5
-    
2
+  border-radius: 20px;
3
+  padding: 0 0 0 0;
6
 }
4
 }
7
 
5
 
8
 .ListItemText {
6
 .ListItemText {
9
-    font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
-}
7
+  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
8
+  width: 100%;
9
+}
10
+
11
+ion-list-header {
12
+  /* color: "primary"; */
13
+}

+ 0
- 27
src/components/ListComponent.tsx Просмотреть файл

1
-import { IonItem, IonLabel, IonList, IonListHeader } from "@ionic/react";
2
-import "../components/ListComponent.css";
3
-
4
-const ListComponent: React.FC<{ title?: string; items: string[] }> = (
5
-  props
6
-) => {
7
-  const title = props.title;
8
-  const items = props.items;
9
-  return (
10
-    <IonList>
11
-      {title ? (
12
-        <IonListHeader>
13
-          <IonLabel>{title}</IonLabel>
14
-        </IonListHeader>
15
-      ) : null}
16
-
17
-      {items.map((itemName: string) => {
18
-        return (
19
-          <IonItem routerLink={`/article/${encodeURIComponent(itemName)}`}>
20
-            <p className="ListItemText">{itemName}</p>
21
-          </IonItem>
22
-        );
23
-      })}
24
-    </IonList>
25
-  );
26
-};
27
-export default ListComponent;

+ 12
- 0
src/components/SkeletonText.tsx Просмотреть файл

1
+import { IonSkeletonText } from "@ionic/react";
2
+import React from "react";
3
+
4
+function skeletonRange() {
5
+  return Math.floor(Math.random() * (75 - 25 + 1)) + 25;
6
+}
7
+
8
+export default function SkeletonText() {
9
+  return (
10
+    <IonSkeletonText animated={true} style={{ width: `${skeletonRange()}%` }} />
11
+  );
12
+}

+ 13
- 0
src/components/TopicList.css Просмотреть файл

1
+.list {
2
+  border-radius: 20px;
3
+  padding: 0 0 0 0;
4
+}
5
+
6
+.ListItemText {
7
+  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
8
+  width: 100%;
9
+}
10
+
11
+ion-list-header {
12
+  /* color: "primary"; */
13
+}

+ 61
- 0
src/components/TopicList.tsx Просмотреть файл

1
+import {
2
+  IonContent,
3
+  IonItem,
4
+  IonLabel,
5
+  IonList,
6
+  IonListHeader,
7
+} from "@ionic/react";
8
+import { Topic } from "../types";
9
+import SkeletonText from "./SkeletonText";
10
+import "../components/TopicList.css";
11
+
12
+/**
13
+ * This component creates a shimmering "skeleton" version of the list component.
14
+ * It shows only when content isn't available yet (loading).
15
+ */
16
+function ListSkeleton({ items = 5 }: { items?: number }) {
17
+  return (
18
+    <>
19
+      {Array(items)
20
+        .fill(0)
21
+        .map((_, i) => (
22
+          <IonItem key={i}>
23
+            <SkeletonText />
24
+          </IonItem>
25
+        ))}
26
+    </>
27
+  );
28
+}
29
+
30
+const TopicList: React.FC<{
31
+  title?: string;
32
+  topics: Topic[];
33
+  loading?: boolean;
34
+}> = ({ title, topics, loading }) => {
35
+  console.log(topics);
36
+  return (
37
+    <IonList>
38
+      {title ? (
39
+        <IonListHeader>
40
+          <IonLabel>{title}</IonLabel>
41
+        </IonListHeader>
42
+      ) : null}
43
+
44
+      {loading && <ListSkeleton />}
45
+
46
+      {!loading &&
47
+        topics &&
48
+        topics.map((topic: Topic) => {
49
+          return (
50
+            <IonItem
51
+              key={topic.blockId}
52
+              routerLink={`/article/${topic.blockId}`}
53
+            >
54
+              <p className="ListItemText">{topic.name}</p>
55
+            </IonItem>
56
+          );
57
+        })}
58
+    </IonList>
59
+  );
60
+};
61
+export default TopicList;

+ 1
- 0
src/config.ts Просмотреть файл

1
+export const API_URL = process.env.API_URL ?? "http://localhost:3000";

+ 4
- 0
src/lib/api.ts Просмотреть файл

1
+import { API_URL } from "../config";
2
+
3
+export const fetcher = (url: string) =>
4
+  fetch(`${API_URL}${url}`).then((r) => r.json());

+ 72
- 0
src/pages/AboutListPage.tsx Просмотреть файл

1
+import {
2
+  IonContent,
3
+  IonHeader,
4
+  IonImg,
5
+  IonItem,
6
+  IonLabel,
7
+  IonList,
8
+  IonListHeader,
9
+  IonPage,
10
+  IonTitle,
11
+  IonToolbar,
12
+} from "@ionic/react";
13
+import ButtonComponent from "../components/ButtonComponent"
14
+
15
+const AboutListPage: React.FC = () => {
16
+  let body= "Es de mi interés ponerme en contacto con ustedes para poder coordinar el uso de uno de sus servicios. También me gustaría saber donde están actualmente ubicados."
17
+  return (
18
+    <IonPage>
19
+      <IonHeader>
20
+        <IonToolbar>
21
+          <IonTitle>About</IonTitle>
22
+        </IonToolbar>
23
+      </IonHeader>
24
+      <IonContent fullscreen>
25
+        <IonHeader collapse="condense">
26
+          <IonToolbar>
27
+            <IonTitle size="large">About</IonTitle>
28
+          </IonToolbar>
29
+        </IonHeader>
30
+        <IonContent className="ion-padding">
31
+          <div className="video">
32
+            <iframe
33
+              src="https://www.youtube.com/embed/MRo8FR2l6VM"
34
+              title="YouTube video player"
35
+              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
36
+            ></iframe>
37
+          </div>
38
+          <h5>Bienvenidos</h5>
39
+          <p>
40
+            Renacer Social ha iniciado una campaña de orientación a la comunidad
41
+            para que los niños y niñas puedan denunciar su situación de
42
+            maltrato. Con una seña discreta que consta de una “X” formada frente
43
+            al torso con los dedos índice, el menor puede dar alerta de su
44
+            situación de maltrato. Es responsabilidad de todos denunciar la
45
+            posible situación de maltrato si vemos a un menor realizar dicha
46
+            seña.
47
+          </p>
48
+          <IonList>
49
+            <IonListHeader>
50
+              <IonLabel>Servicios</IonLabel>
51
+            </IonListHeader>
52
+            <IonItem routerLink={`/services/casita`}>
53
+              <p className="ListItemText">Casita de Paz</p>
54
+            </IonItem>
55
+            <IonItem routerLink={`/services/Crecemos`}>
56
+              <p className="ListItemText">Crecemos</p>
57
+            </IonItem>
58
+            <IonItem routerLink={`/services/Lazos`}>
59
+              <p className="ListItemText">Proyecto Lazos</p>
60
+            </IonItem>
61
+            <IonItem routerLink={`/services/Supervisadas`}>
62
+              <p className="ListItemText">Visitas Supervisadas</p>
63
+            </IonItem>
64
+          </IonList>
65
+          <ButtonComponent body={body}></ButtonComponent>
66
+        </IonContent>
67
+      </IonContent>
68
+    </IonPage>
69
+  );
70
+};
71
+
72
+export default AboutListPage;

+ 45
- 17
src/pages/AdviceListPage.tsx Просмотреть файл

3
   IonHeader,
3
   IonHeader,
4
   IonPage,
4
   IonPage,
5
   IonTitle,
5
   IonTitle,
6
+  IonToast,
6
   IonToolbar,
7
   IonToolbar,
7
 } from "@ionic/react";
8
 } from "@ionic/react";
8
-import DropdownComponent from "../components/DropdownComponent";
9
-import "../types";
10
-
11
-const categories = [
12
-  {
13
-    name: "Paternidad",
14
-    listItems: ["Consejo de Paternidad 1", "Consejo de Paternidad 2"],
15
-  },
16
-  {
17
-    name: "Abuso Sexual",
18
-    listItems: ["Consejo de Abuso Sexual 1", "Consejo de Abuso Sexual 2"],
19
-  },
20
-];
9
+import { alertCircleOutline } from "ionicons/icons";
10
+import useSWR from "swr";
11
+import TopicList from "../components/TopicList";
12
+import { fetcher } from "../lib/api";
13
+import { Topic } from "../types";
21
 
14
 
22
 const AdviceListPage: React.FC = () => {
15
 const AdviceListPage: React.FC = () => {
16
+  const { data: paternidad, error: paternidadError } = useSWR<Topic[]>(
17
+    "/api/getTopicList?type=advice&category=Paternidad",
18
+    fetcher
19
+  );
20
+  const { data: matrimonio, error: matrimonioError } = useSWR<Topic[]>(
21
+    "/api/getTopicList?type=advice&category=Matrimonio",
22
+    fetcher
23
+  );
24
+  const { data: abuso, error: abusoError } = useSWR<Topic[]>(
25
+    "/api/getTopicList?type=advice&category=Abuso%20Sexual",
26
+    fetcher
27
+  );
28
+  const error = paternidadError || matrimonioError || abusoError;
29
+
23
   return (
30
   return (
24
     <IonPage>
31
     <IonPage>
25
       <IonHeader>
32
       <IonHeader>
26
         <IonToolbar>
33
         <IonToolbar>
27
-          <IonTitle>Consejos</IonTitle>
34
+          <IonTitle>Advice</IonTitle>
28
         </IonToolbar>
35
         </IonToolbar>
29
       </IonHeader>
36
       </IonHeader>
30
       <IonContent fullscreen>
37
       <IonContent fullscreen>
31
         <IonHeader collapse="condense">
38
         <IonHeader collapse="condense">
32
           <IonToolbar>
39
           <IonToolbar>
33
-            <IonTitle size="large">Consejos</IonTitle>
40
+            <IonTitle size="large">Advice</IonTitle>
34
           </IonToolbar>
41
           </IonToolbar>
35
         </IonHeader>
42
         </IonHeader>
36
-        <DropdownComponent category={categories} />
43
+        <TopicList
44
+          title="Paternidad"
45
+          loading={!paternidad && !paternidadError}
46
+          topics={paternidad ? (paternidad as Topic[]) : []}
47
+        />
48
+        <TopicList
49
+          title="Matrimonio"
50
+          loading={!matrimonio && !matrimonioError}
51
+          topics={matrimonio ? (matrimonio as Topic[]) : []}
52
+        />
53
+        <TopicList
54
+          title="Abuso Sexual"
55
+          loading={!abuso && !abusoError}
56
+          topics={abuso ? (abuso as Topic[]) : []}
57
+        />
58
+        <IonToast
59
+          icon={alertCircleOutline}
60
+          color="danger"
61
+          isOpen={error !== undefined}
62
+          message="Failed to load items."
63
+          duration={1500}
64
+        />
37
       </IonContent>
65
       </IonContent>
38
     </IonPage>
66
     </IonPage>
39
   );
67
   );
40
 };
68
 };
41
 
69
 
42
-export default AdviceListPage;
70
+export default AdviceListPage;

+ 48
- 17
src/pages/ArticlePage.tsx Просмотреть файл

13
   IonToolbar,
13
   IonToolbar,
14
 } from "@ionic/react";
14
 } from "@ionic/react";
15
 import { RouteComponentProps } from "react-router";
15
 import { RouteComponentProps } from "react-router";
16
+import { NotionRenderer } from "react-notion-x";
17
+import { fetcher } from "../lib/api";
18
+import useSWR from "swr";
19
+import { formatDate, getBlockTitle, getPageProperty } from "notion-utils";
20
+import SkeletonText from "../components/SkeletonText";
16
 
21
 
17
-const ArticlePage: React.FC<RouteComponentProps<{ slug: string }>> = ({
22
+const ArticlePage: React.FC<RouteComponentProps<{ articleId: string }>> = ({
18
   match,
23
   match,
19
 }) => {
24
 }) => {
25
+  const { data, error } = useSWR<any>(
26
+    `/api/getTopic?id=${match.params.articleId}`,
27
+    fetcher
28
+  );
29
+  const isLoading = !data && !error;
30
+  const keys = Object.keys(data?.block || {});
31
+  const block = data?.block?.[keys[0]]?.value;
32
+  const title = data && getBlockTitle(block, data);
33
+  const date =
34
+    data &&
35
+    formatDate(block?.last_edited_time, {
36
+      month: "long",
37
+    });
38
+  const recordMap = data;
39
+  console.log(recordMap);
40
+
20
   return (
41
   return (
21
     <IonPage>
42
     <IonPage>
22
       <IonHeader>
43
       <IonHeader>
24
           <IonButtons slot="start">
45
           <IonButtons slot="start">
25
             <IonBackButton />
46
             <IonBackButton />
26
           </IonButtons>
47
           </IonButtons>
27
-          <IonTitle>{match.params.slug}</IonTitle>
48
+          <IonTitle>
49
+            {data ? getBlockTitle(block, data) : <SkeletonText />}
50
+          </IonTitle>
28
         </IonToolbar>
51
         </IonToolbar>
29
       </IonHeader>
52
       </IonHeader>
30
       <IonContent fullscreen>
53
       <IonContent fullscreen>
31
-        <IonCard>
32
-          <img
33
-            alt="Silhouette of mountains"
34
-            src="https://ionicframework.com/docs/img/demos/card-media.png"
35
-          />
36
-          <IonCardHeader>
37
-            <IonCardTitle>{match.params.slug}</IonCardTitle>
38
-            <IonCardSubtitle>Card Subtitle</IonCardSubtitle>
39
-          </IonCardHeader>
40
-
41
-          <IonCardContent>
42
-            Here's a small text description for the card content. Nothing more,
43
-            nothing less.
44
-          </IonCardContent>
45
-        </IonCard>
54
+        <IonCardHeader className="notion">
55
+          <IonCardTitle>{title ?? <SkeletonText />}</IonCardTitle>
56
+          <IonCardSubtitle>{date ?? <SkeletonText />}</IonCardSubtitle>
57
+        </IonCardHeader>
58
+        <IonCardContent>
59
+          {!recordMap && (
60
+            <div>
61
+              <SkeletonText />
62
+              <SkeletonText />
63
+              <SkeletonText />
64
+              <SkeletonText />
65
+              <SkeletonText />
66
+              <SkeletonText />
67
+            </div>
68
+          )}
69
+          {recordMap && (
70
+            <NotionRenderer
71
+              recordMap={recordMap}
72
+              fullPage={true}
73
+              darkMode={false}
74
+            />
75
+          )}
76
+        </IonCardContent>
46
       </IonContent>
77
       </IonContent>
47
     </IonPage>
78
     </IonPage>
48
   );
79
   );

+ 15
- 8
src/pages/Home.tsx Просмотреть файл

5
   IonTitle,
5
   IonTitle,
6
   IonToolbar,
6
   IonToolbar,
7
 } from "@ionic/react";
7
 } from "@ionic/react";
8
-import "../components/ListComponent";
9
-import ListComponent from "../components/ListComponent";
10
 
8
 
11
 const Home: React.FC = () => {
9
 const Home: React.FC = () => {
12
   return (
10
   return (
23
           </IonToolbar>
21
           </IonToolbar>
24
         </IonHeader>
22
         </IonHeader>
25
         <div className="ion-padding">
23
         <div className="ion-padding">
26
-          <h5>Bienvenidos</h5>
24
+          <div className="video">
25
+            <iframe
26
+              src="https://www.youtube.com/embed/xCtgekNpxI4"
27
+              title="YouTube video player"
28
+              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
29
+            ></iframe>
30
+          </div>
31
+          <h2>Bienvenidos</h2>
27
           <p>
32
           <p>
28
             Somos una organización Lorem Ipsum is simply dummy text of the
33
             Somos una organización Lorem Ipsum is simply dummy text of the
29
             printing and typesetting industry. Lorem Ipsum.
34
             printing and typesetting industry. Lorem Ipsum.
30
           </p>
35
           </p>
31
-        </div>
32
 
36
 
33
-        <ListComponent
34
-          items={["Articulo 1", "Articulo 2"]}
35
-          title="Articulos Recientes"
36
-        />
37
+          <h3>Si se encuentra en peligro llame al 9-1-1 o al 787-343-2020</h3>
38
+          <p>
39
+            Si usted se encuentra en peligro inminente, llame a la Policía de
40
+            Puerto Rico al 9-1-1 o al 787-343-2020. Debe buscar protección para
41
+            usted y los suyos de manera inmediata. NO ESPERE.
42
+          </p>
43
+        </div>
37
       </IonContent>
44
       </IonContent>
38
     </IonPage>
45
     </IonPage>
39
   );
46
   );

+ 20
- 2
src/pages/LawListPage.tsx Просмотреть файл

3
   IonHeader,
3
   IonHeader,
4
   IonPage,
4
   IonPage,
5
   IonTitle,
5
   IonTitle,
6
+  IonToast,
6
   IonToolbar,
7
   IonToolbar,
7
 } from "@ionic/react";
8
 } from "@ionic/react";
8
-import ListComponent from "../components/ListComponent";
9
+import { alertCircleOutline } from "ionicons/icons";
10
+import useSWR from "swr";
11
+import TopicList from "../components/TopicList";
12
+import { fetcher } from "../lib/api";
13
+import { Topic } from "../types";
9
 
14
 
10
 const LawListPage: React.FC = () => {
15
 const LawListPage: React.FC = () => {
16
+  const { data, error } = useSWR<Topic[]>(
17
+    "/api/getTopicList?type=laws",
18
+    fetcher
19
+  );
20
+  const isLoading = !data && !error;
21
+
11
   return (
22
   return (
12
     <IonPage>
23
     <IonPage>
13
       <IonHeader>
24
       <IonHeader>
21
             <IonTitle size="large">Leyes</IonTitle>
32
             <IonTitle size="large">Leyes</IonTitle>
22
           </IonToolbar>
33
           </IonToolbar>
23
         </IonHeader>
34
         </IonHeader>
24
-        <ListComponent items={["Law 1", "Law 2"]} />
35
+        <TopicList loading={isLoading} topics={data ? (data as Topic[]) : []} />
36
+        <IonToast
37
+          icon={alertCircleOutline}
38
+          color="danger"
39
+          isOpen={error !== undefined}
40
+          message="Failed to load items."
41
+          duration={1500}
42
+        />
25
       </IonContent>
43
       </IonContent>
26
     </IonPage>
44
     </IonPage>
27
   );
45
   );

+ 46
- 0
src/pages/services/CasitaPage.tsx Просмотреть файл

1
+import {
2
+  IonBackButton,
3
+  IonButtons,
4
+  IonCard,
5
+  IonCardContent,
6
+  IonCardHeader,
7
+  IonCardSubtitle,
8
+  IonCardTitle,
9
+  IonContent,
10
+  IonHeader,
11
+  IonPage,
12
+  IonTitle,
13
+  IonToolbar,
14
+} from "@ionic/react";
15
+import { RouteComponentProps } from "react-router";
16
+
17
+const CasitaPage: React.FC = () => {
18
+  return (
19
+    <IonPage>
20
+      <IonHeader>
21
+        <IonToolbar>
22
+          <IonButtons slot="start">
23
+            <IonBackButton />
24
+          </IonButtons>
25
+          <IonTitle>Servicios</IonTitle>
26
+        </IonToolbar>
27
+      </IonHeader>
28
+      <IonContent fullscreen>
29
+        <IonCard>
30
+          <img
31
+            alt="Silhouette of mountains"
32
+            src="https://ionicframework.com/docs/img/demos/card-media.png"
33
+          />
34
+          <IonCardHeader>
35
+            <IonCardTitle>Casita de Paz</IonCardTitle>
36
+            <IonCardSubtitle></IonCardSubtitle>
37
+          </IonCardHeader>
38
+
39
+          <IonCardContent>Romper ciclos negativos</IonCardContent>
40
+        </IonCard>
41
+      </IonContent>
42
+    </IonPage>
43
+  );
44
+};
45
+
46
+export default CasitaPage;

+ 46
- 0
src/pages/services/CrecemosPage.tsx Просмотреть файл

1
+import {
2
+  IonBackButton,
3
+  IonButtons,
4
+  IonCard,
5
+  IonCardContent,
6
+  IonCardHeader,
7
+  IonCardSubtitle,
8
+  IonCardTitle,
9
+  IonContent,
10
+  IonHeader,
11
+  IonPage,
12
+  IonTitle,
13
+  IonToolbar,
14
+} from "@ionic/react";
15
+import { RouteComponentProps } from "react-router";
16
+
17
+const CrecemosPage: React.FC = () => {
18
+  return (
19
+    <IonPage>
20
+      <IonHeader>
21
+        <IonToolbar>
22
+          <IonButtons slot="start">
23
+            <IonBackButton />
24
+          </IonButtons>
25
+          <IonTitle>Servicios</IonTitle>
26
+        </IonToolbar>
27
+      </IonHeader>
28
+      <IonContent fullscreen>
29
+        <IonCard>
30
+          <img
31
+            alt="Silhouette of mountains"
32
+            src="https://ionicframework.com/docs/img/demos/card-media.png"
33
+          />
34
+          <IonCardHeader>
35
+            <IonCardTitle>Crecemos</IonCardTitle>
36
+            <IonCardSubtitle></IonCardSubtitle>
37
+          </IonCardHeader>
38
+
39
+          <IonCardContent>Educacion Co-parental</IonCardContent>
40
+        </IonCard>
41
+      </IonContent>
42
+    </IonPage>
43
+  );
44
+};
45
+
46
+export default CrecemosPage;

+ 46
- 0
src/pages/services/LazosPage.tsx Просмотреть файл

1
+import {
2
+  IonBackButton,
3
+  IonButtons,
4
+  IonCard,
5
+  IonCardContent,
6
+  IonCardHeader,
7
+  IonCardSubtitle,
8
+  IonCardTitle,
9
+  IonContent,
10
+  IonHeader,
11
+  IonPage,
12
+  IonTitle,
13
+  IonToolbar,
14
+} from "@ionic/react";
15
+import { RouteComponentProps } from "react-router";
16
+
17
+const LazosPage: React.FC = () => {
18
+  return (
19
+    <IonPage>
20
+      <IonHeader>
21
+        <IonToolbar>
22
+          <IonButtons slot="start">
23
+            <IonBackButton />
24
+          </IonButtons>
25
+          <IonTitle>Servicios</IonTitle>
26
+        </IonToolbar>
27
+      </IonHeader>
28
+      <IonContent fullscreen>
29
+        <IonCard>
30
+          <img
31
+            alt="Silhouette of mountains"
32
+            src="https://ionicframework.com/docs/img/demos/card-media.png"
33
+          />
34
+          <IonCardHeader>
35
+            <IonCardTitle>Proyecto de Lazos</IonCardTitle>
36
+            <IonCardSubtitle></IonCardSubtitle>
37
+          </IonCardHeader>
38
+
39
+          <IonCardContent>Maltrato de Parejas</IonCardContent>
40
+        </IonCard>
41
+      </IonContent>
42
+    </IonPage>
43
+  );
44
+};
45
+
46
+export default LazosPage;

+ 48
- 0
src/pages/services/SupervisadasPage.tsx Просмотреть файл

1
+import {
2
+  IonBackButton,
3
+  IonButtons,
4
+  IonCard,
5
+  IonCardContent,
6
+  IonCardHeader,
7
+  IonCardSubtitle,
8
+  IonCardTitle,
9
+  IonContent,
10
+  IonHeader,
11
+  IonPage,
12
+  IonTitle,
13
+  IonToolbar,
14
+} from "@ionic/react";
15
+import { RouteComponentProps } from "react-router";
16
+
17
+const SupervisadasPage: React.FC = () => {
18
+  return (
19
+    <IonPage>
20
+      <IonHeader>
21
+        <IonToolbar>
22
+          <IonButtons slot="start">
23
+            <IonBackButton />
24
+          </IonButtons>
25
+          <IonTitle>Servicios</IonTitle>
26
+        </IonToolbar>
27
+      </IonHeader>
28
+      <IonContent fullscreen>
29
+        <IonCard>
30
+          <img
31
+            alt="Silhouette of mountains"
32
+            src="https://ionicframework.com/docs/img/demos/card-media.png"
33
+          />
34
+          <IonCardHeader>
35
+            <IonCardTitle>Visitas Supervisadas</IonCardTitle>
36
+            <IonCardSubtitle></IonCardSubtitle>
37
+          </IonCardHeader>
38
+
39
+          <IonCardContent>
40
+            Espacio tranquilo y seguro para visitas supervisadas
41
+          </IonCardContent>
42
+        </IonCard>
43
+      </IonContent>
44
+    </IonPage>
45
+  );
46
+};
47
+
48
+export default SupervisadasPage;

+ 37
- 0
src/theme/global.css Просмотреть файл

1
+.notion-nav-header,
2
+.notion-title,
3
+.notion-header {
4
+  display: none !important;
5
+}
6
+
7
+.notion-page {
8
+  width: 100% !important;
9
+  padding: 0px 0px 0px 0px !important;
10
+}
11
+
12
+main.notion-page-no-cover {
13
+  padding-top: 0px !important;
14
+  margin-top: 0px !important;
15
+}
16
+
17
+.notion-blank {
18
+  display: none !important;
19
+}
20
+
21
+.video {
22
+  position: relative;
23
+  overflow: hidden;
24
+  width: 100%;
25
+  padding-top: 56.25%; /* 16:9 Aspect Ratio (divide 9 by 16 = 0.5625) */
26
+}
27
+
28
+/* Then style the iframe to fit in the container div with full height and width */
29
+.video iframe {
30
+  position: absolute;
31
+  top: 0;
32
+  left: 0;
33
+  bottom: 0;
34
+  right: 0;
35
+  width: 100%;
36
+  height: 100%;
37
+}

+ 2
- 16
src/theme/variables.css Просмотреть файл

76
   --ion-color-light-tint: #f5f6f9;
76
   --ion-color-light-tint: #f5f6f9;
77
 }
77
 }
78
 
78
 
79
+/*
79
 @media (prefers-color-scheme: dark) {
80
 @media (prefers-color-scheme: dark) {
80
-  /*
81
-   * Dark Colors
82
-   * -------------------------------------------
83
-   */
84
-
85
   body {
81
   body {
86
     --ion-color-primary: #428cff;
82
     --ion-color-primary: #428cff;
87
     --ion-color-primary-rgb: 66,140,255;
83
     --ion-color-primary-rgb: 66,140,255;
147
     --ion-color-light-tint: #383a3e;
143
     --ion-color-light-tint: #383a3e;
148
   }
144
   }
149
 
145
 
150
-  /*
151
-   * iOS Dark Theme
152
-   * -------------------------------------------
153
-   */
154
-
155
   .ios body {
146
   .ios body {
156
     --ion-background-color: #000000;
147
     --ion-background-color: #000000;
157
     --ion-background-color-rgb: 0,0,0;
148
     --ion-background-color-rgb: 0,0,0;
190
     --ion-toolbar-border-color: var(--ion-color-step-250);
181
     --ion-toolbar-border-color: var(--ion-color-step-250);
191
   }
182
   }
192
 
183
 
193
-
194
-  /*
195
-   * Material Design Dark Theme
196
-   * -------------------------------------------
197
-   */
198
-
199
   .md body {
184
   .md body {
200
     --ion-background-color: #121212;
185
     --ion-background-color: #121212;
201
     --ion-background-color-rgb: 18,18,18;
186
     --ion-background-color-rgb: 18,18,18;
234
     --ion-card-background: #1e1e1e;
219
     --ion-card-background: #1e1e1e;
235
   }
220
   }
236
 }
221
 }
222
+*/

+ 8
- 4
src/types.ts Просмотреть файл

1
-export interface Category {
2
-    name: string;
3
-    listItems: string[];
4
-}
1
+export interface Topic {
2
+  blockId: string;
3
+  name: string;
4
+}
5
+
6
+export interface Error {
7
+  status: string;
8
+}