Você já ouviu falar de uma ferramenta como a Airtable, mas não sabia por onde começar? Então, nós o convidamos para o mundo da programação visual da construção de um banco de dados!
Com esta postagem, iniciamos uma série de tutoriais nos quais daremos exemplos práticos de como trabalhar com nossa ferramenta Quarkly. Neste tutorial, faremos um aplicativo web simples que exibirá os funcionários da empresa. Ao criar o aplicativo, nem um único funcionário da Russian Railways ficou ferido.
O front será feito usando o Quarkly e os dados serão puxados do banco de dados para o Airtable. Na saída, obtemos um aplicativo react sincronizado com o banco de dados.
Preâmbulo. Por que Airtable
Airtable é uma ferramenta popular sem código, onde você pode tornar seus bancos de dados grandes. Eles se parecem com tabelas, mas têm uma funcionalidade muito mais poderosa. Em particular, para nossa lição, Airtable foi escolhido devido à maneira fácil de transferir dados por meio da API.
Se esta é a primeira vez que você ouve falar da Airtable, antes de começar a trabalhar não será supérfluo ler o manual oficial no site da empresa. Também aconselhamos você a não hesitar e fazer perguntas no telegrama Airtable Chat & Community .
A parte do front-end do trabalho será feita no Quarkly, e para isso usaremos apenas dois componentes:
- Cartão de funcionário . Ele conterá uma foto, dados de texto e dois botões: enviar um e-mail e ligar. O cartão receberá esses dados do componente pai - o invólucro.
- Wrapper . Ele receberá dados da Airtable, gerará cartões e transferirá dados para eles.
Para quem não tem tempo de se aprofundar no post no formato impresso, preparamos um vídeo com legendas e timecodes:
Parte 1. Criação de recursos visuais no Quarkly
Criação de cartão:
- Quarkly, Airtable Example;
- ;
- . + Team;

- (StackItem) ;
«» Convert to Component. EmployeeCard.

- react-, -.
:
- . , ;

- EmployeeCard Stack. Stack , EmployeeCard: , «» Convert to Component. EmployeeTable.
, . Airtable.
2. Airtable
Airtable /.
- Add a base, . Start with a template;

- HR & Recruiting Employee directory. Use template;

- ;

, .
3. API
Airtable API. , , Airtable .
- API : https://airtable.com/api

- Employee directory. AUTHENTICATION.

- , EXAMPLE USING BEARER TOKEN (RECOMMENDED).
:
$ curl https://api.airtable.com/v0/app2MdLITmRTBsrkg/Employee%20directory \
-H "Authorization: Bearer YOUR_API_KEY"
- YOUR_API_KEY. , . .

- API Generate API key;

- . 3. .
4. Airtable Quarkly
EmployeeTable , API.
- . Components <> EmployeeTable ( );

- :

- :
import React from "react";
:
import React, { useEffect, useState } from "react";
useEffect useState, ; - , EmployeeCard:
import EmployeeCard from "./EmployeeCard";
- children ( ) override (, ):
const EmployeeTable = props => { const { children, rest } = useOverrides(props, overrides, defaultProps);
:
const EmployeeTable = props => { const { override, rest } = useOverrides(props, overrides, defaultProps);
- useState, :
const [employees, setEmployees] = useState([]);
- useEffect, API Airtable setEmployees.
, . fetch URL , ?view=All%20employees. headers API , 3 , 4.
useEffect(() => { fetch("https://api.airtable.com/v0/appWw7KBKSc9bPjZE/Employee%20directory?view=All%20employees", { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }) .then(response => response.json()) .then(data => setEmployees(data.records.map(({ fields }) => fields))); }, []);
- , props override. , .
:
return <Stack {...rest}> {children} </Stack>; };
:
return <Stack {...rest}> { employees.map(employee => <EmployeeCard {...override("employeeCard")} employee={employee} />) } </Stack>; };
- Ctrl + S ( Cmd + S Mac). :
import React, { useEffect, useState } from "react"; import { useOverrides, Stack } from "@quarkly/components"; import EmployeeCard from "./EmployeeCard"; const defaultProps = { "margin-top": "40px" }; const overrides = {}; const EmployeeTable = props => { const { override, rest } = useOverrides(props, overrides, defaultProps); const [employees, setEmployees] = useState([]); useEffect(() => { fetch("https://api.airtable.com/v0/appWw7KBKSc9bPjZE/Employee%20directory?view=All%20employees", { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }) .then(response => response.json()) .then(data => setEmployees(data.records.map(({ fields }) => fields))); }, []); return <Stack {...rest}> { employees.map(employee => <EmployeeCard {...override("employeeCard")} employee={employee} />) } </Stack>; }; Object.assign(EmployeeTable, { ...Stack, defaultProps, overrides }); export default EmployeeTable;
: API YOUR_API_KEY.
! Airtable, employees map. employees <EmployeeCard/>, .
EmpolyeeCard .
5. EmpolyeeCard
.
- . Components, EmployeeCard, <>.
- :
import React from "react"; import { useOverrides, Override, StackItem } from "@quarkly/components"; import { Box, Text } from "@quarkly/widgets"; const defaultProps = { "width": "25%", "lg-width": "50%", "sm-width": "100%" }; const overrides = { "box": { "kind": "Box", "props": { "height": "0", "margin": "0 0 20px 0", "padding-bottom": "100%", "background": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat" } }, "text": { "kind": "Text", "props": { "color": "--grey", "margin": "0", "children": "CEO" } }, "text1": { "kind": "Text", "props": { "as": "h3", "font": "--headline3", "margin": "5px 0 20px 0", "children": "Nathan K. Joe" } }, "text2": { "kind": "Text", "props": { "as": "p", "margin": "20px 0 5px 0", "children": "This space is 100% editable. Use it to introduce a team member, describe their work experience and role within the company. This is also a great place to highlight a team member's strong sides." } } }; const EmployeeCard = props => { const { override, children, rest } = useOverrides(props, overrides, defaultProps); return <StackItem {...rest}> <Override slot="StackItemContent" flex-direction="column" /> <Box {...override("box")} /> <Text {...override("text")} /> <Text {...override("text1")} /> <Text {...override("text2")} /> {children} </StackItem>; }; Object.assign(EmployeeCard, { ...StackItem, defaultProps, overrides }); export default EmployeeCard;
- :
} = useOverrides(props, overrides, defaultProps);
:
const { employee = {} } = rest;
employee . - , , . :
<Box {...override("box")} />
:
<Box {...override("box")} background-image={`url(${employee.Photo && employee.Photo[0] && employee.Photo[0].url})`}/>
:
"background": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat"
:
"background-size": "cover", "background-position": "center", "background-image": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat"
:

- , . API Airtable . https://airtable.com/api, .
EMPLOYEE DIRECTORY TABLE.
, :
Name
Department
Home address
Email address
DOB
Start date
Phone
Reports to
Title
Status
Photo
Location - Title. :
<Text {...override("text")} />
:
<Text {...override("title")} children={employee.Title} />
overrides , .
:
"text": { "kind": "Text", "props": { "color": "--grey", "margin": "0", "children": "CEO" } },
:
"title": { "kind": "Text", "props": { "color": "--grey", "margin": "0", "children": "Title" } },
:
: . - Name Home address.
:
<Text {...override("text1")} /> <Text {...override("text2")} />
:
<Text {...override("name")} children={employee.Name} /> <Text {...override("address")} children={employee['Home address']} />
overrides. :
"text1": { "kind": "Text", "props": { "as": "h3", "font": "--headline3", "margin": "5px 0 20px 0", "children": "Nathan K. Joe" } }, "text2": { "kind": "Text", "props": { "as": "p", "margin": "20px 0 5px 0", "children": "This space is 100% editable. Use it to introduce a team member, describe their work experience and role within the company. This is also a great place to highlight a team member's strong sides." } }
:
"name": { "kind": "Text", "props": { "as": "h3", "font": "--headline3", "margin": "5px 0 5px 0", "children": "Name" } }, "address": { "kind": "Text", "props": { "as": "p", "margin": "10px 0 5px 0", "children": "Home address" } },
:

- Text . Department Reports to, DEPARTMENTS TABLE.
:
<Text {...override("address")} children={employee['Home address']} /> <Text {...override("Start date")} children={`Start date: ${employee['Start date']}`} /> <Text {...override("Status")} children={employee['Status']} /> <Text {...override("DOB")} children={`Birth date: ${employee['DOB']}`} />
"address": { "kind": "Text", "props": { "as": "p", "margin": "10px 0 5px 0", "children": "Home address" } }, "Start date": { "kind": "Text", "props": { "as": "p", "margin": "10px 0 5px 0", "children": "Start date" } }, "Status": { "kind": "Text", "props": { "as": "p", "margin": "10px 0 5px 0", "children": "Status" } }, "DOB": { "kind": "Text", "props": { "as": "p", "margin": "10px 0 5px 0", "children": "Birth date" } },
:

- Agora vamos adicionar dois componentes de Link , nos quais teremos telefone e e- mail :
import { Box, Text } from "@quarkly/widgets";
mudar para:
import { Box, Text, Link } from "@quarkly/widgets";
E adicione as seguintes linhas:
<Link {...override("Email address")} children={employee['Email address']} href={`mailto:${employee['Email address']}`} /> <Link {...override("Phone")} children={employee['Phone']} href={`tel:${employee['Phone']}`}/>
Não esquecendo de suas substituições :
"Email address": { "kind": "Link", "props": { "margin": "10px 0 5px 0", "color": "--primary", "text-decoration": "none", "children": "Email" } }, "Phone": { "kind": "Link", "props": { "margin": "10px 0 5px 0", "color": "--primary", "text-decoration": "none", "children": "Phone" } },
Verificando o resultado:

Nosso código final se parece com este:
import React from "react";
import { useOverrides, Override, StackItem } from "@quarkly/components";
import { Box, Text, Link } from "@quarkly/widgets";
const defaultProps = {
"width": "25%",
"lg-width": "50%",
"sm-width": "100%"
};
const overrides = {
"box": {
"kind": "Box",
"props": {
"height": "0",
"margin": "0 0 20px 0",
"padding-bottom": "100%",
"background-size": "cover",
"background-position": "center",
"background-image": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat"
}
},
"title": {
"kind": "Text",
"props": {
"color": "--grey",
"margin": "0",
"children": "title"
}
},
"name": {
"kind": "Text",
"props": {
"as": "h3",
"font": "--headline3",
"margin": "5px 0 5px 0",
"children": "Name"
}
},
"address": {
"kind": "Text",
"props": {
"as": "p",
"margin": "10px 0 5px 0",
"children": "Home address"
}
},
"Start date": {
"kind": "Text",
"props": {
"as": "p",
"margin": "10px 0 5px 0",
"children": "Start date"
}
},
"Status": {
"kind": "Text",
"props": {
"as": "p",
"margin": "10px 0 5px 0",
"children": "Status"
}
},
"DOB": {
"kind": "Text",
"props": {
"as": "p",
"margin": "10px 0 5px 0",
"children": "Birth date"
}
},
"Email address": {
"kind": "Link",
"props": {
"margin": "10px 0 5px 0",
"color": "--primary",
"text-decoration": "none",
"children": "Email"
}
},
"Phone": {
"kind": "Link",
"props": {
"margin": "10px 0 5px 0",
"color": "--primary",
"text-decoration": "none",
"children": "Phone"
}
},
};
const EmployeeCard = props => {
const {
override,
children,
rest
} = useOverrides(props, overrides, defaultProps);
const { employee = {} } = rest;
return <StackItem {...rest}>
<Override slot="StackItemContent" flex-direction="column" />
<Box {...override("box")} background-image={`url(${employee.Photo[0].url})`}/>
<Text {...override("title")} children={employee.Title} />
<Text {...override("name")} children={employee.Name} />
<Text {...override("address")} children={employee['Home address']} />
<Text {...override("Start date")} children={`Start date: ${employee['Start date']}`} />
<Text {...override("Status")} children={employee['Status']} />
<Text {...override("DOB")} children={`Birth date: ${employee['DOB']}`} />
<Link {...override("Email address")} children={employee['Email address']} href={`mailto:${employee['Email address']}`} />
<Link {...override("Phone")} children={employee['Phone']} href={`tel:${employee['Phone']}`}/>
{children}
</StackItem>;
};
Object.assign(EmployeeCard, { ...StackItem,
defaultProps,
overrides
});
export default EmployeeCard;
Comprometemo-nos com o GitHub e publicamos no Netlify:
Esperamos alguns minutos e verificamos: https://keen-varahamihira-c54ae1.netlify.app/

Para verificar a sincronização, altere os dados no banco de dados:

Eles agora aparecerão no aplicativo:

No futuro, podemos estilizar nossos elementos com cartões como quisermos, sem quebrar a importação configurada de Airtable. Um exemplo pode ser visto aqui .
Repositório GitHub: https://github.com/quarkly-dev/Getting-data-from-Airtable-tutorial
Obrigado pela atenção!
— . , , .