Technologiczne, Gadżety, Telefony Komórkowe, Pobieranie Aplikacji!

Jak utworzyć rozszerzenie Chrome do zarządzania wtyczkami swojej witryny za pomocą Kinsta API i React

Jako użytkownik Google Chrome prawdopodobnie używałeś niektórych rozszerzeń w tej przeglądarce. Czy kiedykolwiek zastanawiałeś się, jak są zbudowane lub czy możesz zbudować jedno?

W tym artykule dowiesz się, jak utworzyć rozszerzenie przeglądarki Chrome, a konkretnie takie, które wykorzystuje React i API Kinsta do zarządzania wtyczkami w witrynach WordPress hostowanych przy użyciu Kinsta.

Czym jest rozszerzenie Chrome?

Rozszerzenie Chrome to program zainstalowany w przeglądarce Chrome, który zwiększa jej funkcjonalność. Rozszerzenia mogą obejmować zarówno proste przyciski ikon na pasku narzędzi, jak i w pełni zintegrowane funkcje, które głęboko oddziałują na Twoje przeglądanie.

Jak utworzyć rozszerzenie Chrome

Tworzenie rozszerzenia Chrome jest podobne do tworzenia aplikacji internetowej, ale wymaga pliku w formacie JSON o nazwie manifest.json. Ten plik działa jako kręgosłup rozszerzenia, dyktując jego ustawienia, uprawnienia i funkcjonalności, które chcesz uwzględnić.

Na początek utwórz folder, który będzie zawierał wszystkie pliki rozszerzeń. Następnie utwórz manifest.json plik w folderze.

Podstawowa manifest.json plik rozszerzenia Chrome zawiera kluczowe właściwości, które definiują podstawowe ustawienia rozszerzenia. Poniżej znajduje się przykład manifest.json plik zawierający pola niezbędne do prawidłowego działania:

{
  "manifest_version": 3,
  "name": "My Chrome extension",
  "version": "1.0",
  "description": "Here is a description for my Chrome extension."
}

Możesz załadować i przetestować to jako rozpakowane rozszerzenie do Chrome. Przejdź do chrome://extensions w swojej przeglądarce i przełącz Tryb programistya następnie kliknij Załaduj rozpakowane przycisk. Spowoduje to otwarcie przeglądarki plików i możesz wybrać katalog, który utworzyłeś dla swojego rozszerzenia.

Załaduj rozszerzenie Chrome, klikając opcję Załaduj rozpakowane w trybie programisty.

Po kliknięciu ikony rozszerzenia nic się nie dzieje, ponieważ nie utworzyłeś interfejsu użytkownika.

Utwórz interfejs użytkownika (wyskakujące okienko) dla rozszerzenia Chrome

Podobnie jak w przypadku każdej aplikacji internetowej, interfejs użytkownika (UI) rozszerzenia wykorzystuje kod HTML do strukturyzowania treści, kod CSS do jej stylizacji oraz kod JavaScript do dodawania interaktywności.

Stwórzmy podstawowy interfejs użytkownika, używając wszystkich tych plików. Zacznijmy od utworzenia pliku HTML (wyskakujące okienko.html). Ten plik definiuje strukturę elementów interfejsu użytkownika, takich jak tekst, nagłówki, obrazy i przyciski. Dodaj następujący kod:


    
        
        
        Hello World
        
    
    
        
        

My first Chrome Extension

id="sayHello">Say Hello

Powyższy kod tworzy nagłówek, akapit i przycisk. Pliki CSS i JavaScript są również połączone. Teraz dodaj kilka stylów w wyskakujące okienko.css plik:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: Arial, sans-serif;
    background-color: aliceblue;
    padding: 20px;
}

Następnie w wyskakujące okienko.js plik, dodaj obiekt nasłuchujący zdarzeń do przycisku, tak aby po jego kliknięciu wyświetlał się alert:

const sayHelloBtn = document.getElementById('sayHello');
sayHelloBtn.addEventListener('click', async () => {
    let tab = await chrome.tabs.query({ active: true });
    chrome.scripting.executeScript({
        target: { tabId: tab[0].id },
        function: () => alert('Hello from the extension!'),
    });
});

Ten kod JavaScript pobiera aktualnie aktywną kartę i używa API skryptów Chrome aby wykonać skrypt wyświetlający alert z komunikatem powitalnym, gdy Powiedz cześć przycisk jest kliknięty. Wprowadza to podstawową interaktywność do rozszerzenia Chrome.

Dzięki tym krokom utworzyłeś prosty interfejs użytkownika dla swojego rozszerzenia Chrome, który zawiera podstawowy tekst, styl i funkcjonalność.

Na koniec musisz włączyć plik popup w manifest.json plik dodając pewne uprawnienia:

{
    . . . ,
    "action": {
        "default_popup": "popup.html"
    },
    "permissions": [
        "scripting",
        "tabs"
    ],
    "host_permissions": [
        "http://*/*",
        "https://*/*"
    ]
}

W powyższej konfiguracji klucz default_popup określa, że wyskakujące okienko.html będzie domyślnym interfejsem użytkownika, gdy użytkownik wchodzi w interakcję z rozszerzeniem. Tablica uprawnień obejmuje skrypty i karty, które są kluczowe dla rozszerzenia, aby wchodziło w interakcję z kartami i używało funkcji skryptowych przeglądarki.

Tablica host_permissions określa, z którymi witrynami rozszerzenie może wchodzić w interakcje. Wzory http://*/* i https://*/* wskazują, że rozszerzenie może wchodzić w interakcje ze wszystkimi witrynami dostępnymi za pośrednictwem protokołów HTTP i HTTPS.

Dzięki tym ustawieniom w Twoim manifest.json plik, rozszerzenie Chrome jest prawidłowo skonfigurowane, aby wyświetlać wyskakujące okienko i uruchamiać skrypty.

Załaduj ponownie rozszerzenie Chrome

Po wprowadzeniu tych zmian w folderze lokalnym musisz zaktualizować rozpakowany folder załadowany do Chrome. Aby to zrobić, otwórz stronę rozszerzeń Chrome, znajdź swoje rozszerzenie i kliknij ikonę ponownego ładowania.

Kliknij ikonę odświeżania, aby ponownie załadować rozszerzenie.

Następnie możesz kliknąć ikonę rozszerzenia, a pojawi się okno podręczne. Po kliknięciu Powiedz cześć przycisk, a pojawi się alert.

Masz teraz podstawową wiedzę na temat tego, jak zacząć budować rozszerzenie Chrome. Można zrobić więcej. Możesz manipulować interfejsem użytkownika swojej witryny, tworzyć żądania API, pobierać dane z adresów URL, aby wykonywać określone operacje i wiele więcej.

Jak utworzyć rozszerzenie Chrome za pomocą React

Jak wspomnieliśmy wcześniej, tworzenie rozszerzenia Chrome jest podobne do budowania aplikacji internetowej. Możesz używać popularnych frameworków internetowych, takich jak React.

W przypadku Reacta manifest.json plik jest tworzony w publiczny folder. Ten folder jest używany do statycznych zasobów, których nie chcesz przetwarzać za pomocą Webpacka (lub podobnych bundlerów, których React może używać w tle w narzędziach takich jak Utwórz aplikację React).

Podczas tworzenia aplikacji React proces tworzenia kopiuje całą jej zawartość publiczny włóż do folderu dyst. folder. Oto jak utworzyć rozszerzenie Chrome z React:

  1. Utwórz nową aplikację React. Możesz użyć lokalnego środowiska programistycznego Vite, uruchamiając następujące polecenie w terminalu:
npm create vite@latest

Następnie nadaj swojemu projektowi nazwę i wybierz React jako framework. Po wykonaniu tej czynności przejdź do folderu projektu i zainstaluj zależności:

cd 
npm install
  1. W Twoim projekcie React publiczny folder, utwórz manifest.json plik. Dodaj następujące konfiguracje:
{
    "manifest_version": 3,
    "name": "React Chrome extension",
    "description": "Chrome extension built with React",
    "version": "0.1.0",
    "action": {
        "default_popup": "index.html"
    },
    "permissions": [
        "tabs"
    ],
    "host_permissions": [
        "http://*/*",
        "https://*/*"
    ]
}

Konfiguracja rozszerzenia Chrome obejmuje obiekt akcji, który ustawia indeks.html jako domyślne okno pop-up po kliknięciu ikony rozszerzenia. Jest to statyczny plik HTML generowany podczas tworzenia aplikacji React.

  1. Rozwijaj aplikację React. Możesz swobodnie tworzyć żądania API, stylizować je według własnego uznania, używać React Hooks i nie tylko.
  1. Gdy skończysz budować interfejs użytkownika rozszerzenia, uruchom polecenie build w React (npm run build). Wszystkie zasoby, w tym manifest.json plik, wygenerowany przez React indeks.htmli inne są przenoszone do dyst. Lub zbudować falcówka.
  2. Na koniec załaduj rozszerzenie do Chrome. Przejdź do chrome://extensions/ i ponownie załaduj rozszerzenie.

Tworzenie rozszerzenia Chrome do zarządzania wtyczkami witryny za pomocą interfejsu API Kinsta

Tak będzie wyglądać rozszerzenie Chrome, które utworzysz:

Rozszerzenie Chrome napisane w React, które współpracuje z API Kinsta.

Po kliknięciu rozszerzenie wyświetla listę witryn z nieaktualnymi wtyczkami na Twoim koncie MyKinsta. Możesz zobaczyć listę wtyczek i kliknąć Zobacz w MyKinsta przycisk, aby przejść do strony Motywy i wtyczki, gdzie można zaktualizować każdą wtyczkę.

Sprawdźmy, jak utworzyć rozszerzenie dla przeglądarki Chrome.

Zrozumienie interfejsu API Kinsta

Kinsta API to potężne narzędzie, które umożliwia programową interakcję z usługami Kinsta, takimi jak hostowane witryny WordPress. Może pomóc zautomatyzować różne zadania związane z zarządzaniem WordPress, w tym tworzenie witryny, pobieranie informacji o witrynie, uzyskiwanie statusu witryny, przeglądanie i przywracanie kopii zapasowych i wiele innych.

Aby korzystać z API Kinsta, musisz mieć konto z co najmniej jedną witryną WordPress, aplikacją lub bazą danych w MyKinsta. Musisz również wygenerować klucz API, aby uwierzytelnić i uzyskać dostęp do swojego konta.

Aby wygenerować klucz API:

  1. Przejdź do pulpitu nawigacyjnego MyKinsta.
  2. Przejdź do strony Klucze API (Twoje imię > Ustawienia firmy > Klucze API).
  3. Trzask Utwórz klucz API.
  4. Wybierz datę wygaśnięcia lub ustaw niestandardową datę rozpoczęcia i liczbę godzin, po których klucz ma wygasnąć.
  5. Nadaj kluczowi unikalną nazwę.
  6. Trzask Spowodować.

Po utworzeniu klucza API skopiuj go i przechowuj w bezpiecznym miejscu (zaleca się korzystanie z menedżera haseł). Możesz wygenerować wiele kluczy API, które zostaną wymienione na liście Klucze API strona. Jeśli musisz cofnąć klucz API, kliknij Unieważnić przycisk.

Zarządzaj wtyczkami swojej witryny za pomocą Kinsta API i React

Zacznijmy od opracowania interfejsu użytkownika w React, który następnie zostanie przekształcony w rozszerzenie Chrome. Ten przewodnik zakłada podstawową znajomość React i interakcji API.

Konfigurowanie środowiska

Po pierwsze, w Aplikacja.jsx plik, zdefiniuj stałą dla adresu URL interfejsu API Kinsta, aby uniknąć powtarzalności w kodzie:

const KinstaAPIUrl="https://api.kinsta.com/v2";

Ze względów bezpieczeństwa przechowuj poufne dane, takie jak klucz API i identyfikator firmy Kinsta w .env.local plik, aby zapewnić im bezpieczeństwo i uniemożliwić dostęp do kodu źródłowego:

VITE_KINSTA_COMPANY_ID=YOUR_COMPANY_ID
VITE_KINSTA_API_KEY=YOUR_API_KEY

Pobierz dane za pomocą interfejsu API Kinsta

W Aplikacja.jsx pliku należy wykonać kilka żądań do interfejsu API Kinsta w celu pobrania informacji o witrynach i ich wtyczkach.

  1. Pobierz witryny firm: Zacznij od pobrania listy witryn powiązanych z kontem firmy Kinsta. Użyj identyfikatora firmy w żądaniu GET, które zwraca tablicę szczegółów witryny.
    const getListOfCompanySites = async () => {
          const query = new URLSearchParams({
            company: import.meta.env.VITE_KINSTA_COMPANY_ID,
          }).toString();
          const resp = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
            method: 'GET',
            headers: {
              Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
            },
          });
          const data = await resp.json();
          const companySites = data.company.sites;
          return companySites;
        }
  2. Pobierz dane środowiskowe dla każdej witryny: Dla każdej witryny pobierz środowiska, które zawierają identyfikator środowiska niezbędny do dalszych żądań. Obejmuje to mapowanie na każdej witrynie i wykonanie wywołania API do punktu końcowego /sites/${siteId}/environments.
     const companySites = await getListOfCompanySites();
        // Get all environments for each site
    
        const sitesEnvironmentData = companySites.map(async (site) => {
          const siteId = site.id;
          const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
            method: 'GET',
            headers: {
              Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
            },
          });
          const data = await resp.json();
          const environments = data.site.environments;
          return {
            id: siteId,
            name: site.display_name,
            environments: environments,
          };
        });
  3. Pobierz wtyczki dla każdego środowiska witryny: Na koniec użyj identyfikatora środowiska, aby pobrać wtyczki dla każdej witryny. Ten krok obejmuje funkcję mapowania i wywołanie API do punktu końcowego /sites/environments/${environmentId}/plugins dla każdego środowiska.
    // Wait for all the promises to resolve
        const sitesData = await Promise.all(sitesEnvironmentData);
    
        // Get all plugins for each environment
        const sitesWithPlugin = sitesData.map(async (site) => {
          const environmentId = site.environments[0].id;
          const resp = await fetch(
            `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
            {
              method: 'GET',
              headers: {
                Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
              },
            }
          );
          const data = await resp.json();
          const plugins = data.environment.container_info;
          return {
            env_id: environmentId,
            name: site.name,
            site_id: site.id,
            plugins: plugins,
          };
        });

    Teraz możesz umieścić wszystkie te żądania razem w funkcji, która służy do zwracania końcowej tablicy witryn z podstawowymi informacjami o każdej witrynie i jej wtyczkach:

    const getSitesWithPluginData = async () => {
      const getListOfCompanySites = async () => {
        const query = new URLSearchParams({
          company: import.meta.env.VITE_KINSTA_COMPANY_ID,
        }).toString();
        const resp = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
          },
        });
        const data = await resp.json();
        const companySites = data.company.sites;
        return companySites;
      }
    
      const companySites = await getListOfCompanySites();
    
      // Get all environments for each site
      const sitesEnvironmentData = companySites.map(async (site) => {
        const siteId = site.id;
        const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
          },
        });
        const data = await resp.json();
        const environments = data.site.environments;
        return {
          id: siteId,
          name: site.display_name,
          environments: environments,
        };
      });
    
      // Wait for all the promises to resolve
      const sitesData = await Promise.all(sitesEnvironmentData);
    
      // Get all plugins for each environment
      const sitesWithPlugin = sitesData.map(async (site) => {
        const environmentId = site.environments[0].id;
        const resp = await fetch(
          `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
          {
            method: 'GET',
            headers: {
              Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
            },
          }
        );
        const data = await resp.json();
        const plugins = data.environment.container_info;
        return {
          env_id: environmentId,
          name: site.name,
          site_id: site.id,
          plugins: plugins,
        };
      });
    
      // Wait for all the promises to resolve
      const sitesWithPluginData = await Promise.all(sitesWithPlugin);
      return sitesWithPluginData;
    }

Wyświetlanie danych witryny

Utwórz stan za pomocą haka useState, aby przechowywać witryny z nieaktualnymi wtyczkami. Hak useEffect wywoła również metodę getSitesWithPluginData() i wyodrębni szczegóły witryny, gdy komponent zostanie zamontowany.

W haku useEffect utwórz funkcję, która będzie przechodzić przez każdą witrynę, aby odfiltrować witryny z nieaktualnymi wtyczkami, a następnie zapisze je w następującym stanie:

const [sitesWithOutdatedPlugin, setSitesWithOutdatedPlugin] = useState([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
  const checkSitesWithPluginUpdate = async () => {
    const sitesWithPluginData = await getSitesWithPluginData();
    const sitesWithOutdatedPlugin = sitesWithPluginData.map((site) => {
      const plugins = site.plugins.wp_plugins.data;
      const outdatedPlugins = plugins.filter((plugin) => plugin.update === "available");
      if (outdatedPlugins.length > 0) {
        const kinstaDashboardPluginPageURL = `https://my.kinsta.com/sites/plugins/${site.site_id}/${site.env_id}?idCompany=${import.meta.env.VITE_KINSTA_COMPANY_ID}`;
        return {
          name: site.name,
          plugins: outdatedPlugins,
          url: kinstaDashboardPluginPageURL,
        };
      }
    });

    setSitesWithOutdatedPlugin(sitesWithOutdatedPlugin);

  checkSitesWithPluginUpdate();
  setIsLoading(false);
}, []);

W powyższym kodzie można zauważyć, że stan ładowania jest również tworzony i domyślnie ustawiany na true. Będzie on używany do kontrolowania sposobu wyświetlania danych. Gdy wszystkie dane zostaną załadowane, ustawiamy go na false.

Poniżej znajduje się znacznik umożliwiający renderowanie danych witryny i wtyczek w interfejsie użytkownika.

import { useEffect, useState } from "react"
import KinstaLogo from './assets/kinsta-logo.png'
import PluginPage from './components/PluginsPage'

function App() {
  // load the data from the API
  return (
    
        
           className="title-section">
            
          
          

className="info-box"> Get quick information about your site plugins that need update.

{isLoading ? (

Loading...

) : ( <>

The following sites have plugins that need to be updated.

{sitesWithOutdatedPlugin.map((site, index) => { return ( ); })} > )} ) } export default App

Kod zawiera nagłówek z logo i akapit informacyjny. Zawartość interfejsu użytkownika jest warunkowo renderowana na podstawie stanu isLoading. Jeśli dane są nadal ładowane, wyświetla komunikat ładowania. Po załadowaniu danych przedstawia dane o witrynach i wszelkich wtyczkach wymagających aktualizacji.

Zauważysz również komponent: PluginPage (Strona wtyczki.jsx). Ten komponent jest przeznaczony do wyświetlania poszczególnych witryn i szczegółów ich wtyczek. Zawiera funkcjonalność przełączania widoczności szczegółów wtyczek.

import { useState } from "react"
import { FaRegEye } from "react-icons/fa";
import { FaRegEyeSlash } from "react-icons/fa";

const PluginUse = (site) => {
    const [viewPlugin, setViewPlugin] = useState(false);

    return (
        <>
            
                
                {viewPlugin && (
                    
                        {site.plugins.map((plugin, index) => {
                            return (
                                
                                    

{plugin.name}

Current Version: {plugin.version}

Latest Version: {plugin.update_version}

); })} )} > ) } export default PluginUse

Informacje

Aby nadać styl swojej aplikacji, skopiuj Kod CSS z naszego kodu źródłowego i wklej do własnego pliku CSS.

Skonfiguruj plik manifestu

Aby przekształcić interfejs użytkownika i jego funkcjonalność w rozszerzenie przeglądarki Chrome, należy skonfigurować manifest.json plik.

Utwórz manifest.json plik w publiczny folder i wklej poniższy kod:

{
    "manifest_version": 3,
    "name": "Kinsta Plugins Manager - Thanks to Kinsta API",
    "description": "This extension allows you to manage your WordPress site's plugin from Kinsta's MyKinsta dashboard via Kinsta API.",
    "version": "0.1.0",
    "icons": {
        "48": "kinsta-icon.png"
    },
    "action": {
        "default_popup": "index.html"
    },
    "permissions": [
        "tabs"
    ],
    "host_permissions": [
        "https://my.kinsta.com/*"
    ]
}

Pamiętaj o dodaniu pliku ikony do swojego publiczny falcówka.

W tym momencie możesz uruchomić polecenie kompilacji (npm run build), aby wszystkie zasoby, w tym manifest.json plik, wygenerowany przez React indeks.htmli inne pliki są przenoszone do dyst. Lub zbudować falcówka.

Następnie przejdź do chrome://extensions/ i załaduj to jako rozpakowane rozszerzenie do Chrome. Kliknij Załaduj rozpakowane i wybierz katalog, który utworzyłeś dla swojego rozszerzenia.

Ogranicz rozszerzenie do określonych witryn

Zauważ, że to rozszerzenie działa w dowolnym momencie. Chcemy, aby działało tylko wtedy, gdy użytkownik zostanie przekierowany do pulpitu MyKinsta.

Aby to zrobić, dostosujmy Aplikacja.jsx plik. Utwórz stan do przechowywania aktywnej karty:

const [activeTab, setActiveTab] = useState(null);

Następnie zaktualizuj hak useEffect, aby zdefiniować i wywołać funkcję getCurrentTab:

const getCurrentTab = async () => {
  const queryOptions = { active: true, currentWindow: true };
  const [tab] = await chrome.tabs.query(queryOptions);
  setActiveTab(tab);
}
getCurrentTab();

Powyższy kod używa chrome.tabs.query ze specyficznymi opcjami zapytania, aby upewnić się, że pobiera tylko aktywną kartę w bieżącym oknie. Po pobraniu karty jest ona ustawiana jako aktywna karta w stanie rozszerzenia.

Na koniec zaimplementuj logikę renderowania warunkowego w instrukcji return swojego komponentu. Dzięki temu interfejs użytkownika zarządzania wtyczkami będzie wyświetlany tylko wtedy, gdy użytkownik będzie na pulpicie MyKinsta:

return (
  
    {activeTab?.url.includes('my.kinsta.com') ? (
      
        
          
        
        

Get quick information about your site plugins that need update.

{isLoading ? (

Loading...

) : ( <>

The following {sitesWithPluginUpdate} sites have plugins that need to be updated.

{sitesWithOutdatedPlugin.map((site, index) => { return ( ); })} > )} ) : (

This extension is only available on Kinsta Dashboard.

)} )

Po wprowadzeniu zmian przebuduj aplikację i ponownie załaduj rozszerzenie Chrome. Spowoduje to zastosowanie nowej logiki i ograniczeń.

Streszczenie

W tym artykule poznałeś podstawy tworzenia rozszerzenia Chrome i jak utworzyć je za pomocą React. Dowiedziałeś się również, jak utworzyć rozszerzenie, które wchodzi w interakcję z API Kinsta.

Jako użytkownik Kinsta możesz wykorzystać ogromny potencjał i elastyczność interfejsu API Kinsta, który ułatwia tworzenie niestandardowych rozwiązań do zarządzania witrynami, aplikacjami i bazami danych.

Z którego punktu końcowego API Kinsta korzystałeś najczęściej i jak go używałeś? Podziel się z nami w sekcji komentarzy!


Oszczędź czas i koszty, a także zmaksymalizuj wydajność witryny dzięki integracjom na poziomie przedsiębiorstwa o wartości ponad 300 USD zawartym w każdym planie Managed WordPress. Obejmuje to wysokowydajną sieć CDN, ochronę przed atakami DDoS, ochronę przed złośliwym oprogramowaniem i hakowaniem, buforowanie brzegowe i najszybsze maszyny CPU Google. Zacznij bez długoterminowych umów, wspomaganych migracji i 30-dniowej gwarancji zwrotu pieniędzy.

Zapoznaj się z naszymi planami lub porozmawiaj z przedstawicielem handlowym, aby znaleźć plan odpowiedni dla siebie.

Joel Olawanle Kinsta

Joel jest programistą front-endu pracującym w Kinsta jako redaktor techniczny. Jest pasjonatem nauczania, który kocha open source i napisał ponad 300 artykułów technicznych, głównie na temat JavaScript i jego frameworków.