Tworzenie i wdrażanie statycznej aplikacji internetowej na platformie Azure

W tym samouczku lokalnie skompiluj i wdróż aplikację kliencką React/TypeScript w statycznej aplikacji internetowej platformy Azure za pomocą akcji GitHub. Aplikacja React umożliwia analizowanie obrazu za pomocą usług Cognitive Services przetwarzanie obrazów.

Tworzenie lub używanie istniejącej subskrypcji platformy Azure

Będziesz potrzebować konta użytkownika platformy Azure z aktywną subskrypcją. Utwórz je bezpłatnie.

Wymagania wstępne

  • Node.js i npm — zainstalowane na komputerze lokalnym.
  • Visual Studio Code — zainstalowany na komputerze lokalnym.
    • Azure Static Web Apps — służy do wdrażania aplikacji React w statycznej aplikacji internetowej platformy Azure.
  • Git — służy do wypychania do usługi GitHub — która aktywuje akcję GitHub.
  • Konto usługi GitHub — aby rozwidlić i wypchnąć do repozytorium
  • Skorzystaj z usługi Azure Cloud Shell za pośrednictwem środowiska bash.
  • Twoje konto platformy Azure musi mieć przypisaną rolę współautora usług Cognitive Services, aby można było zaakceptować odpowiednie warunki korzystania ze sztucznej inteligencji i utworzyć zasób. Aby uzyskać przypisaną tę rolę do konta, wykonaj kroki opisane w dokumentacji Przypisywanie ról lub skontaktuj się z administratorem.

Co to jest statyczna aplikacja internetowa platformy Azure

Podczas tworzenia statycznych aplikacji internetowych możesz wybrać kilka opcji na platformie Azure na podstawie stopnia funkcjonalności i kontroli, którą cię interesują. Ten samouczek koncentruje się na najprostszej usłudze z wieloma wyborami, dzięki czemu możesz skupić się na kodzie frontonu, a nie środowisku hostingu.

Platforma React (create-react-app) udostępnia następujące funkcje:

  • Wyświetl komunikat, jeśli nie znaleziono klucza i punktu końcowego platformy Azure dla usług Cognitive Services przetwarzanie obrazów
  • Umożliwia analizowanie obrazu za pomocą usług Cognitive Services przetwarzanie obrazów
    • Wprowadź publiczny adres URL obrazu lub analizowanie obrazu z kolekcji
    • Po zakończeniu analizy
      • Wyświetl obraz
      • Wyświetlanie wyników przetwarzanie obrazów JSON

Partial browser screenshot of React Cognitive Service Computer Vision sample results.

Aby wdrożyć statyczną aplikację internetową, użyj akcji GitHub, która rozpoczyna się po wypchnięciu do określonej gałęzi:

  • Wstawia wpisy tajne usługi GitHub dla klucza przetwarzanie obrazów i punktu końcowego do kompilacji
  • Kompiluje klienta platformy React (create-react-app)
  • Przenosi pliki wynikowe do zasobu statycznej aplikacji internetowej platformy Azure

1. Rozwidlenie przykładowego repozytorium

Utwórz rozwidlenie repozytorium, a nie klonowanie go na komputerze lokalnym, aby mieć własne repozytorium GitHub w celu wypychania zmian.

  1. Otwórz oddzielne okno przeglądarki lub kartę i zaloguj się do usługi GitHub.

  2. Przejdź do przykładowego repozytorium GitHub.

    https://github.com/Azure-Samples/js-e2e-client-cognitive-services
    
  3. W prawym górnym rogu strony wybierz pozycję Rozwidlenie.

  4. Wybierz pozycję Kod , a następnie skopiuj adres URL lokalizacji rozwidlenia.

    Partial screenshot of GitHub website, select **Code** then copy the location for your fork.

2. Tworzenie lokalnego środowiska projektowego

  1. W oknie terminalu lub powłoki bash sklonuj rozwidlenie na komputer lokalny. Zastąp YOUR-ACCOUNT-NAME ciąg nazwą konta usługi GitHub.

    git clone https://github.com/YOUR-ACCOUNT-NAME/js-e2e-client-cognitive-services
    
  2. Przejdź do nowego katalogu i zainstaluj zależności.

    cd js-e2e-client-cognitive-services && npm install
    

    Krok instalacji instaluje wymagane zależności, w tym @azure/cognitiveservices-computervision.

3. Uruchamianie lokalnego przykładu

  1. Uruchom przykład.

    npm start
    

    Partial browser screenshot of React Cognitive Service Computer Vision sample for image analysis before key and endpoint set.

  2. Zatrzymaj aplikację. Zamknij okno terminalu lub użyj control+c go w terminalu.

4. Tworzenie grupy zasobów

W terminalu lub powłoce bash wprowadź polecenie interfejsu wiersza polecenia platformy Azure, aby utworzyć grupę zasobów platformy Azure o nazwie rg-demo:

az group create \
    --location eastus \
    --name rg-demo \
    --subscription YOUR-SUBSCRIPTION-NAME-OR-ID

5. Tworzenie zasobu przetwarzanie obrazów

Utworzenie grupy zasobów umożliwia łatwe znajdowanie zasobów i usuwanie ich po zakończeniu. Ten typ zasobu wymaga zgody na umowę odpowiedzialnego użytkowania. Skorzystaj z poniższej listy, aby dowiedzieć się, jak szybko utworzyć prawidłowy zasób:

6. Utwórz pierwszy zasób przetwarzanie obrazów

Jeśli jest to twoja pierwsza usługa sztucznej inteligencji, musisz utworzyć usługę za pośrednictwem portalu i wyrazić zgodę na umowę odpowiedzialnego używania w ramach tworzenia tego zasobu. Jeśli nie jest to pierwszy zasób wymagający umowy odpowiedzialnego używania, możesz utworzyć zasób za pomocą interfejsu wiersza polecenia platformy Azure, który znajduje się w następnej sekcji.

Skorzystaj z poniższej tabeli, aby utworzyć zasób w witrynie Azure Portal.

Ustawienie Wartość
Grupa zasobów rg-demo
Nazwisko demo-ComputerVision
SKU S1
Lokalizacja eastus

7. Tworzenie dodatkowego zasobu przetwarzanie obrazów

Uruchom następujące polecenie, aby utworzyć zasób przetwarzanie obrazów:

az cognitiveservices account create \
    --name demo-ComputerVision \
    --resource-group rg-demo \
    --kind ComputerVision \
    --sku S1 \
    --location eastus \
    --yes

8. Uzyskiwanie przetwarzanie obrazów punktu końcowego i kluczy zasobów

  1. W wynikach znajdź i skopiuj element properties.endpoint. Będzie to potrzebne później.

    ...
    "properties":{
        ...
        "endpoint": "https://eastus.api.cognitive.microsoft.com/",
        ...
    }
    ...
    
  2. Uruchom następujące polecenie , aby pobrać klucze.

    az cognitiveservices account keys list \
    --name demo-ComputerVision \
    --resource-group rg-demo
    
  3. Skopiuj jeden z kluczy, które będą potrzebne później.

    {
      "key1": "8eb7f878bdce4e96b26c89b2b8d05319",
      "key2": "c2067cea18254bdda71c8ba6428c1e1a"
    }
    

9. Dodawanie zmiennych środowiskowych do środowiska lokalnego

Aby korzystać z zasobu, kod lokalny musi mieć dostępny klucz i punkt końcowy. Ta baza kodu przechowuje te zmienne środowiskowe:

  • REACT_APP_AZURE_COMPUTER_VISION_KEY
  • REACT_APP_AZURE_COMPUTER_VISION_ENDPOINT
  1. Uruchom następujące polecenie, aby dodać te zmienne do środowiska.

    export REACT_APP_AZURE_COMPUTER_VISION_KEY="REPLACE-WITH-YOUR-KEY"
    export REACT_APP_AZURE_COMPUTER_VISION_ENDPOINT="REPLACE-WITH-YOUR-ENDPOINT"
    

10. Dodawanie zmiennych środowiskowych do środowiska zdalnego

W przypadku korzystania z usługi Azure Static Web Apps zmienne środowiskowe, takie jak wpisy tajne, należy przekazać z akcji GitHub do statycznej aplikacji internetowej. Akcja usługi GitHub kompiluje aplikację, w tym klucz przetwarzanie obrazów i punkt końcowy przekazany z wpisów tajnych usługi GitHub dla tego repozytorium, a następnie wypycha kod ze zmiennymi środowiskowymi do statycznej aplikacji internetowej.

  1. W przeglądarce internetowej w repozytorium GitHub wybierz pozycję Ustawienia, a następnie pozycję Wpisy tajne, a następnie Wpis tajny nowego repozytorium.

    Partial browser screenshot of GitHub repository, creating new repository secret.

  2. Wprowadź tę samą nazwę i wartość punktu końcowego użytego w poprzedniej sekcji. Następnie utwórz kolejny wpis tajny o tej samej nazwie i wartości klucza, który został użyty w poprzedniej sekcji.

    Enter the same name and value for the endpoint. Then create another secret with the same name and value for the key.

11. Uruchamianie lokalnej aplikacji react przy użyciu zasobu ComputerVision

  1. Uruchom ponownie aplikację w wierszu polecenia:

    npm start
    

    Partial browser screenshot of React Cognitive Service Computer Vision sample ready for URL or press enter.

  2. Pozostaw puste pole tekstowe, aby wybrać obraz z katalogu domyślnego, a następnie wybierz przycisk Analizuj .

    Partial browser screenshot of React Cognitive Service Computer Vision sample results.

    Obraz jest wybierany losowo z katalogu obrazów zdefiniowanych w pliku ./src/DefaultImages.js.

  3. Wybierz przycisk Analizuj, aby wyświetlić inne obrazy i wyniki.

12. Wypchnij gałąź lokalną do usługi GitHub

W terminalu programu Visual Studio Code wypchnij gałąź main lokalną do repozytorium zdalnego.

git push origin main

Nie trzeba zatwierdzać żadnych zmian, ponieważ nie wprowadzono jeszcze żadnych zmian.

13. Tworzenie zasobu statycznej aplikacji internetowej

  1. Wybierz ikonę platformy Azure, a następnie kliknij prawym przyciskiem myszy usługę Static Web Apps, a następnie wybierz pozycję Utwórz statyczną aplikację internetową (zaawansowaną).

    Visual Studio Code screenshot with Visual Studio extension

  2. Jeśli zostanie wyświetlone okno podręczne z pytaniem, czy chcesz kontynuować w main gałęzi, wybierz pozycję Kontynuuj.

  3. Wprowadź następujące informacje w kolejnych polach przedstawionych pojedynczo.

    Nazwa pola wartość
    Wybierz grupę zasobów dla nowych zasobów. Wybierz grupę zasobów utworzoną dla zasobu ComputerVision. demo-ComputerVision
    Wprowadź nazwę nowej statycznej aplikacji internetowej. Demo-ComputerVisionAnalyzer
    Wybierz opcję cennika Wybierz pozycję Bezpłatna.
    Wybierz lokalizację kodu aplikacji. Wybierz tę samą lokalizację wybraną podczas tworzenia grupy zasobów. eastus
    Wybierz ustawienie wstępne kompilacji, aby skonfigurować domyślną strukturę projektu. React
    Wybierz lokalizację kodu aplikacji. /
    Wprowadź lokalizację kodu usługi Azure Functions. Przyjmij wartość domyślną.
    Wprowadź ścieżkę danych wyjściowych kompilacji względem lokalizacji aplikacji. build

14. Aktualizowanie akcji usługi GitHub za pomocą tajnych zmiennych środowiskowych

Klucz i punkt końcowy przetwarzanie obrazów znajdują się w kolekcji wpisów tajnych repozytorium, ale nie znajdują się jeszcze w akcji usługi GitHub. Ten krok dodaje klucz i punkt końcowy do akcji.

  1. Pobierz zmiany wprowadzone na podstawie tworzenia zasobu platformy Azure, aby pobrać plik akcji usługi GitHub.

    git pull origin main
    
  2. W edytorze programu Visual Studio Code zmodyfikuj plik akcji usługi GitHub znajdujący się pod adresem ./.github/workflows/ , aby dodać wpisy tajne.

    name: Azure Static Web Apps CI/CD
    
    on:
      push:
        branches:
          - from-local
      pull_request:
        types: [opened, synchronize, reopened, closed]
        branches:
          - from-local
    
    jobs:
      build_and_deploy_job:
        if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
        runs-on: ubuntu-latest
        name: Build and Deploy Job
        steps:
          - uses: actions/checkout@v2
            with:
              submodules: true
          - name: Build And Deploy
            id: builddeploy
            uses: Azure/static-web-apps-deploy@v0.0.1-preview
            with:
              azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_RANDOM_NAME_HERE }}
              repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
              action: "upload"
              ###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
              # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
              app_location: "/" # App source code path
              api_location: "api" # Api source code path - optional
              output_location: "build" # Built app content directory - optional
              ###### End of Repository/Build Configurations ######
            env:
              REACT_APP_AZURE_COMPUTER_VISION_ENDPOINT: ${{secrets.REACT_APP_AZURE_COMPUTER_VISION_ENDPOINT}}
              REACT_APP_AZURE_COMPUTER_VISION_KEY:  ${{secrets.REACT_APP_AZURE_COMPUTER_VISION_KEY}}
    
      close_pull_request_job:
        if: github.event_name == 'pull_request' && github.event.action == 'closed'
        runs-on: ubuntu-latest
        name: Close Pull Request Job
        steps:
          - name: Close Pull Request
            id: closepullrequest
            uses: Azure/static-web-apps-deploy@v0.0.1-preview
            with:
              azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_RANDOM_NAME_HERE }}
              action: "close"
    
  3. Dodaj i zatwierdź zmianę w gałęzi lokalnej main .

    git add . && git commit -m "add secrets to action"
    
  4. Wypchnij zmianę do repozytorium zdalnego, rozpoczynając nową akcję kompilowania i wdrażania w statycznej aplikacji internetowej platformy Azure.

    git push origin main
    

15. Wyświetlanie procesu kompilacji akcji usługi GitHub

  1. W przeglądarce internetowej otwórz repozytorium GitHub na potrzeby tego samouczka i wybierz pozycję Akcje.

  2. Wybierz górną kompilację z listy, a następnie wybierz pozycję Kompiluj i wdróż zadanie w menu po lewej stronie, aby obejrzeć proces kompilacji. Poczekaj na pomyślne zakończenie kompilacji i wdrożenia .

     Select the top build in the list, then select `Build and Deploy Job` on the left-side menu to watch the build process. Wait until the build successfully finishes.

16. Wyświetlanie zdalnej statycznej witryny internetowej platformy Azure w przeglądarce

  1. W programie Visual Studio Code wybierz ikonę platformy Azure w menu po prawej stronie, a następnie wybierz statyczną aplikację internetową, a następnie kliknij prawym przyciskiem myszy pozycję Przeglądaj witrynę, a następnie wybierz pozycję Otwórz, aby wyświetlić publiczną statyczną witrynę internetową.

Select `Browse site`, then select `Open` to view the public static web site.

Adres URL witryny można również znaleźć pod adresem:

  • witryna Azure Portal dla zasobu na stronie Przegląd .
  • dane wyjściowe kompilacji i wdrażania akcji usługi GitHub mają adres URL witryny na samym końcu skryptu

17. Czyszczenie zasobów dla statycznej aplikacji internetowej

Po ukończeniu tego samouczka musisz usunąć grupę zasobów obejmującą zasób przetwarzanie obrazów i statyczną aplikację internetową, aby upewnić się, że nie są naliczane opłaty za więcej użycia.

W programie VS Code wybierz eksploratora platformy Azure, a następnie kliknij prawym przyciskiem myszy grupę zasobów, która znajduje się na liście w ramach subskrypcji, a następnie wybierz pozycję Usuń.

Partial screen shot of VS Code, selecting resource group from list of resource groups, then right-clicking to select `Delete`.

Kod: Dodawanie przetwarzanie obrazów do lokalnej aplikacji React

Użyj narzędzia npm, aby dodać przetwarzanie obrazów do pliku package.json.

npm install @azure/cognitiveservices-computervision 

Kod: Dodawanie kodu przetwarzanie obrazów jako oddzielnego modułu

Kod przetwarzanie obrazów znajduje się w osobnym pliku o nazwie ./src/azure-cognitiveservices-computervision.js. Wyróżniono główną funkcję modułu.

// ./src/azure-cognitiveservices-computervision.js

// Azure SDK client libraries
import { ComputerVisionClient } from '@azure/cognitiveservices-computervision';
import { ApiKeyCredentials } from '@azure/ms-rest-js';

// List of sample images to use in demo
import RandomImageUrl from './DefaultImages';

// Authentication requirements
const key = process.env.REACT_APP_AZURE_COMPUTER_VISION_KEY;
const endpoint = process.env.REACT_APP_AZURE_COMPUTER_VISION_ENDPOINT;

console.log(`key = ${key}`)
console.log(`endpoint = ${endpoint}`)

// Cognitive service features
const visualFeatures = [
    "ImageType",
    "Faces",
    "Adult",
    "Categories",
    "Color",
    "Tags",
    "Description",
    "Objects",
    "Brands"
];

export const isConfigured = () => {
    const result = (key && endpoint && (key.length > 0) && (endpoint.length > 0)) ? true : false;
    console.log(`key = ${key}`)
    console.log(`endpoint = ${endpoint}`)
    console.log(`ComputerVision isConfigured = ${result}`)
    return result;
}

// Computer Vision detected Printed Text
const includesText = async (tags) => {
    return tags.filter((el) => {
        return el.name.toLowerCase() === "text";
    });
}
// Computer Vision detected Handwriting
const includesHandwriting = async (tags) => {
    return tags.filter((el) => {
        return el.name.toLowerCase() === "handwriting";
    });
}
// Wait for text detection to succeed
const wait = (timeout) => {
    return new Promise(resolve => {
        setTimeout(resolve, timeout);
    });
}

// Analyze Image from URL
export const computerVision = async (url) => {

    // authenticate to Azure service
    const computerVisionClient = new ComputerVisionClient(
        new ApiKeyCredentials({ inHeader: { 'Ocp-Apim-Subscription-Key': key } }), endpoint);

    // get image URL - entered in form or random from Default Images
    const urlToAnalyze = url || RandomImageUrl();
    
    // analyze image
    const analysis = await computerVisionClient.analyzeImage(urlToAnalyze, { visualFeatures });

    // text detected - what does it say and where is it
    if (includesText(analysis.tags) || includesHandwriting(analysis.tags)) {
        analysis.text = await readTextFromURL(computerVisionClient, urlToAnalyze);
    }

    // all information about image
    return { "URL": urlToAnalyze, ...analysis};
}
// analyze text in image
const readTextFromURL = async (client, url) => {
    
    let result = await client.read(url);
    let operationID = result.operationLocation.split('/').slice(-1)[0];

    // Wait for read recognition to complete
    // result.status is initially undefined, since it's the result of read
    const start = Date.now();
    console.log(`${start} -${result?.status} `);
    
    while (result.status !== "succeeded") {
        await wait(500);
        console.log(`${Date.now() - start} -${result?.status} `);
        result = await client.getReadResult(operationID);
    }
    
    // Return the first page of result. 
    // Replace[0] with the desired page if this is a multi-page file such as .pdf or.tiff.
    return result.analyzeResult; 
}

Kod: Dodawanie wykazu obrazów jako oddzielnego modułu

Aplikacja wybiera losowy obraz z wykazu, jeśli użytkownik nie wprowadza adresu URL obrazu. Funkcja wyboru losowego jest wyróżniona

// ./src/DefaultImages.js

const describeURL = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/celebrities.jpg';
const categoryURLImage = 'https://moderatorsampleimages.blob.core.windows.net/samples/sample16.png';
const tagsURL = 'https://moderatorsampleimages.blob.core.windows.net/samples/sample16.png';
const objectURL = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-node-sdk-samples/master/Data/image.jpg';
const brandURLImage = 'https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/images/red-shirt-logo.jpg';
const facesImageURL = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/faces.jpg';
const printedTextSampleURL = 'https://moderatorsampleimages.blob.core.windows.net/samples/sample2.jpg';
const multiLingualTextURL = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/MultiLingual.png';
const adultURLImage = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/celebrities.jpg';
const colorURLImage = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/celebrities.jpg';
// don't use with picture analysis
// eslint-disable-next-line
const mixedMultiPagePDFURL = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/MultiPageHandwrittenForm.pdf';
const domainURLImage = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/landmark.jpg';
const typeURLImage = 'https://raw.githubusercontent.com/Azure-Samples/cognitive-services-python-sdk-samples/master/samples/vision/images/make_things_happen.jpg';

const DefaultImages = [
    describeURL,
    categoryURLImage,
    tagsURL,
    objectURL,
    brandURLImage,
    facesImageURL,
    adultURLImage,
    colorURLImage,
    domainURLImage,
    typeURLImage,
    printedTextSampleURL,
    multiLingualTextURL,
    //mixedMultiPagePDFURL
];

const RandomImageUrl = () => {
    return DefaultImages[Math.floor(Math.random() * Math.floor(DefaultImages.length))];
}

export default RandomImageUrl;

Kod: Dodawanie niestandardowego modułu przetwarzanie obrazów do aplikacji React

Dodaj metody do platformy React app.js. Wyróżniono analizę obrazu i wyświetlanie wyników.

// ./src/App.js

import React, { useState } from 'react';
import './App.css';
import { computerVision, isConfigured as ComputerVisionIsConfigured } from './azure-cognitiveservices-computervision';

function App() {

  const [fileSelected, setFileSelected] = useState(null);
  const [analysis, setAnalysis] = useState(null);
  const [processing, setProcessing] = useState(false);
  
  const handleChange = (e) => {
    setFileSelected(e.target.value)
  }
  const onFileUrlEntered = (e) => {

    // hold UI
    setProcessing(true);
    setAnalysis(null);

    computerVision(fileSelected || null).then((item) => {
      // reset state/form
      setAnalysis(item);
      setFileSelected("");
      setProcessing(false);
    });

  };

  // Display JSON data in readable format
  const PrettyPrintJson = (data) => {
    return (<div><pre>{JSON.stringify(data, null, 2)}</pre></div>);
  }

  const DisplayResults = () => {
    return (
      <div>
        <h2>Computer Vision Analysis</h2>
        <div><img src={analysis.URL} height="200" border="1" alt={(analysis.description && analysis.description.captions && analysis.description.captions[0].text ? analysis.description.captions[0].text : "can't find caption")} /></div>
        {PrettyPrintJson(analysis)}
      </div>
    )
  };
  
  const Analyze = () => {
    return (
    <div>
      <h1>Analyze image</h1>
      {!processing &&
        <div>
          <div>
            <label>URL</label>
            <input type="text" placeholder="Enter URL or leave empty for random image from collection" size="50" onChange={handleChange}></input>
          </div>
          <button onClick={onFileUrlEntered}>Analyze</button>
        </div>
      }
      {processing && <div>Processing</div>}
      <hr />
      {analysis && DisplayResults()}
      </div>
    )
  }
  
  const CantAnalyze = () => {
    return (
      <div>Key and/or endpoint not configured in ./azure-cognitiveservices-computervision.js</div>
    )
  }
  
  function Render() {
    const ready = ComputerVisionIsConfigured();
    if (ready) {
      return <Analyze />;
    }
    return <CantAnalyze />;
  }

  return (
    <div>
      {Render()}
    </div>
    
  );
}

export default App;

Następne kroki