Делаем страницу на React с базой сотрудников при помощи Airtable и Quarkly

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Слышали про такой инструмент, как Airtable, но не знали, с чего начать? Тогда приглашаем в мир визуального программирования построения БД!


Этим постом мы начинаем цикл обучающих статей, в которых будем давать практические примеры работы с нашим инструментом Quarkly. В этом уроке мы сделаем простое веб-приложение, которое будет показывать сотрудников компании. При создании приложения ни один сотрудник РЖД не пострадал.


Фронт будем делать при помощи Quarkly, а данные подтягивать из базы в Airtable. На выходе получим react-приложение, синхронизированное с базой данных.



Преамбула. Почему Airtable


Airtable — популярный no-code инструмент, в котором вы можете делать свои базы данных большого объема. Выглядят они как таблицы, но имеют гораздо более мощный функционал. В частности, для нашего урока выбор Airtable обусловлен легким способом передачи данных по API.


Если вы впервые слышите про Airtable, перед началом работы не будет лишним почитать официальное руководство на сайте компании. Также советуем не стесняться и задавать вопросы в чатике Airtable Chat & Community в телеграм.


Фронтендная часть работ будет сделана в Quarkly, и для этого мы будем использовать всего два компонента:


  • Карточка сотрудника. В ней будут фото, текстовые данные и две кнопки: отправить email и позвонить. Эти данные карточка будет получать от родительского компонента — обертки.
  • Обертка. Она будет принимать данные из Airtable, генерировать карточки и передавать в них данные.

Для тех, у кого нет времени вникать в пост в печатном формате, мы подготовили видео с субтитрами и таймкодами:




Часть 1. Делаем визуал в Quarkly


Создание карточки:


  1. Создадим новый проект в Quarkly, назовем его Airtable Example;
  2. Перейдем внутрь проекта;
  3. Добавим готовый блок с карточками сотрудников. Для этого кликаем на черную кнопку + посередине и выбираем блок из категории Team;



  4. Выбираем на панели слоев первую карточку (StackItem) и преобразуем её в компонент;



    Для этого нажмите на «троеточие» и выберите пункт Convert to Component. Назовем этот компонент EmployeeCard.

  5. Теперь мы можем свободно редактировать код этого react-компонента, но пока этого делать не будем и перейдем к созданию компонента-обертки.

Создание обертки:


  1. Подготовим обертку к преобразованию в компонент. Для этого сначала удалим оставшиеся три карточки, они нам не нужны;

  2. Теперь вытащим EmployeeCard из Stack. Затем преобразуем Stack в компонент, как мы уже делали ранее с EmployeeCard: правая панель, кнопка «троеточие» и Convert to Component. Компонент назовем EmployeeTable.



    На этом пока всё, подготовительный этап завершен. Оставим компоненты и займемся базой в Airtable.


Часть 2. Создаем базу данных в Airtable


Переходим на сайт Airtable и регистрируемся/авторизуемся.


  1. Кликаем на кнопку Add a base, чтобы создать новую базу. Из выпадающего меню выберите пункт Start with a template;

  2. Выбираем категорию HR & Recruiting и шаблон Employee directory. Далее кликаем на кнопку Use template;

  3. Переходим в созданный проект;


На данном этапе здесь мы ничего менять не будем, в текущем виде база уже нас устраивает.



Часть 3. Получаем доступ к API


Изначально Airtable интересен для нас именно за счет удобного API. При этом, что немаловажно, возможность забирать данные и отправлять их в нашу базу Airtable предоставляет бесплатно.


  1. Переходим на страницу выбора API проектов: https://airtable.com/api

  2. Выбираем наш проект Employee directory. В появившейся документации переходим на раздел AUTHENTICATION.

  3. Скопируйте две строчки, расположенные ниже заголовка EXAMPLE USING BEARER TOKEN (RECOMMENDED).

    У меня они выглядят так:

    $ curl https://api.airtable.com/v0/app2MdLITmRTBsrkg/Employee%20directory \
    -H "Authorization: Bearer YOUR_API_KEY"
  4. Теперь нам нужен YOUR_API_KEY. Это уникальный ключ доступа, который генерируется для каждого аккаунта. Найти его можно в настройках.

  5. На открывшейся странице перейдите в раздел API и нажмите на кнопку Generate API key;

  6. Скопируйте ключ. Сохраните его рядом со строчками из пункта 3. Они пригодятся нам далее.


Часть 4. Интегрируем базу Airtable в Quarkly


На этом этапе мы добавим в компонент EmployeeTable код, который будет забирать данные из API.


  1. Переходим в редактирование кода компонента. Для этого откроем вкладку Components и нажмем на кнопку <> у EmployeeTable (она появится при наведении курсора на компонент);

  2. Сейчас код компонента выглядит так:

  3. Заменим:

    import React from "react";

    на:

    import React, { useEffect, useState } from "react";

    Таким образом мы импортируем хуки useEffect и useState, которые помогут нам в дальнейшем;
  4. Ниже добавляем строчку, чтобы импортировать наш компонент EmployeeCard:

    import EmployeeCard from "./EmployeeCard";
  5. Заменим children (они нам пока не нужны) на override (пригодятся, чтобы выбирать элементы и стилизовать их на странице):

    const EmployeeTable = props => {
    	const {
    		children,
    		rest
    	} = useOverrides(props, overrides, defaultProps);

    на:

    const EmployeeTable = props => {
    	const {
    		override,
    		rest
    	} = useOverrides(props, overrides, defaultProps);
  6. Ниже добавим вызов хука useState, который будет следить за состоянием:

    const [employees, setEmployees] = useState([]);
  7. Далее добавим хук 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)));
    	}, []);
  8. Теперь будем генерировать карточки из полученных данных, передавая им props с данными и override. Он нужен, чтобы выбирать и стилизовать элементы на странице.

    Меняем:

    return <Stack {...rest}>
    		{children}
    	</Stack>;
    };

    на:

    return <Stack {...rest}>
    		{
    			employees.map(employee => <EmployeeCard  {...override("employeeCard")}  employee={employee} />)
    		}
    	</Stack>;
    };
  9. Нажмите 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 работать с БД


Здесь мы научим карточку сотрудника принимать данные и показывать их.


  1. Откроем код компонента. Для этого заходим во вкладку Components, ищем там EmployeeCard, наводим курсор и жмем на кнопку <>.
  2. Сейчас код компонента выглядит так:

    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;
  3. Ищем строчку:

    } = useOverrides(props, overrides, defaultProps);

    и добавляем ниже:

    const { employee = {} } = rest;

    В объект employee помещаем наши данные.
  4. На примере фотографии сотрудника проверим, что всё работает, как нужно. Ищем строку и меняем:

    <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"

    Должно получится так:

  5. Смотрим, какие поля у нас есть. Документация для 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
  6. Добавим 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"
    	}
    },

    Сохраняем и проверяем:



    Результат: в карточки добавилась строка с профессией.
  7. Повторим такие же действия для 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"
    	}
    },

    Сохраняем и снова проверяем:

  8. Добавим ещё несколько 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"
    	}
    },

    Проверяем результат:

  9. Теперь добавим два компонента Link, в которых у нас будут Phone и Email:

    import { Box, Text } from "@quarkly/widgets";

    меняем на:

    import { Box, Text, Link } from "@quarkly/widgets";

    И добавляем следующие строки:

    <Link {...override("Email address")} children={employee['Email address']} href={`mailto:${employee['Email address']}`} />
    <Link {...override("Phone")} children={employee['Phone']} href={`tel:${employee['Phone']}`}/>

    Не забыв про их overrides:

    "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"
    	}
    },

    Проверяем результат:


Финально наш код выглядит так:

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;

Делаем коммит в GitHub и публикуем на Netlify:





Ждем несколько минут и проверяем: https://keen-varahamihira-c54ae1.netlify.app/



Для проверки синхронизации меняем данные в базе:


Теперь они появятся в приложении:


В дальнейшем мы можем как угодно стилизовать наши элементы с карточками, не нарушая настроенный импорт из Airtable. Пример можно посмотреть здесь.


Репозиторий на GitHub: https://github.com/quarkly-dev/Getting-data-from-Airtable-tutorial


Спасибо за внимание!


Если у вас остались вопросы — не стесняйтесь задавать их в комментариях. В следующем уроке рассмотрим ещё один пример работы с данными, покажем как интерактивно их визуализировать, дав возможность пользователю менять фильтры прямо в приложении.

Источник: https://habr.com/ru/company/quarkly/blog/540094/


Интересные статьи

Интересные статьи

В обычных наушниках без дополнительной обработки музыка звучит плоско. Кажется, что источник звука - в голове. Попытаемся это исправить! Read more ...
Дисклеймер: данная статья является адаптированным переводом. Оригинал можно прочесть здесь. Байесовские сети при помощи Питона — объяснение с примерами Из-за ограниченности инфор...
Доброго времени суток, Хабр! Все reactjs разработчики, кто имеет дело с интерактивностью между бэком и фронтом рано или поздно встречаются, или встречались, или встретятся со следующей ошибкой...
Cтатья будет полезна тем, кто думает какую выбрать CMS для интернет-магазина, сравнивает различные движки, ищет в них плюсы и минусы важные для себя.
Сегодня мы поговорим о перспективах становления Битрикс-разработчика и об этапах этого пути. Статья не претендует на абсолютную истину, но даёт жизненные ориентиры.