Merge branch 'rc' of https://github.com/rowyio/rowy into rc

This commit is contained in:
shamsmosowi
2021-12-01 14:43:31 +07:00
14 changed files with 120 additions and 70 deletions

View File

@@ -9,7 +9,7 @@
Build on Google Cloud Platform in minutes
</h3>
<p align="center">
Manage Firestore data in a spreadsheet-like UI, write Cloud Functions effortlessly in the browser, and connect to your favorite third party platforms such as SendGrid, Twilio, Algolia, sSack and more.
Manage Firestore data in a spreadsheet-like UI, write Cloud Functions effortlessly in the browser, and connect to your favorite third party platforms such as SendGrid, Twilio, Algolia, Slack and more.
</p>
<div align="center">
@@ -31,7 +31,10 @@ Manage Firestore data in a spreadsheet-like UI, write Cloud Functions effortless
<img width="100%" src="https://firebasestorage.googleapis.com/v0/b/rowyio.appspot.com/o/publicDemo%2FRowy%20Website%20Video%20GIF%20Small.gif?alt=media&token=3f699a8f-c1f2-4046-8ed5-e4ff66947cd8" />
## Quick try
## Live Demo
💥 Check out the [live demo](https://demo.rowy.io/) of Rowy 💥
## Quick Deploy
Set up Rowy on your Google Cloud Platform project with this one-click deploy
button.
@@ -53,7 +56,7 @@ You can find the full documentation with how-to guides
- Multiple views for the same collection
- Bulk import or export data - csv, json
### Supercharge your database with cloud functions and ready made extension
### Automate with cloud functions and ready made extensions
- Effortlessly build cloud functions on field level triggers right in the browser
- Use any NPM modules or APIs
@@ -82,7 +85,7 @@ You can find the full documentation with how-to guides
## Install
Set up Rowy on your Google Cloud project with this one-click deploy button.
Set up Rowy on your Google Cloud project with this one-click deploy button. Your data and cloud functions stay on your own Firestore/GCP.
[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?git_repo=https://github.com/rowyio/rowyRun.git)
@@ -96,6 +99,12 @@ access to your service account or any of your data.
Alternatively, you can manually install by
[following this guide](https://docs.rowy.io/setup/install).
## Roadmap
[View our roadmap](https://demo.rowy.io/table/roadmap) on Rowy - Upvote, downvote, share your thoughts!
If you'd like to propose a feature, submit an issue [here](https://github.com/rowyio/rowy/issues/new?assignees=&labels=&template=feature_request.md&title=).
## Join our community
We would love to connect and hear what you think!

View File

@@ -1,6 +1,6 @@
{
"name": "Rowy",
"version": "2.2.0",
"version": "2.2.1",
"homepage": "https://rowy.io",
"repository": {
"type": "git",
@@ -64,7 +64,7 @@
"react-usestateref": "^1.0.5",
"serve": "^11.3.2",
"swr": "^1.0.1",
"tinymce": "^5.9.2",
"tinymce": "^5.10.0",
"typescript": "^4.4.2",
"use-algolia": "^1.4.1",
"use-debounce": "^3.3.0",

5
src/atoms/Table.ts Normal file
View File

@@ -0,0 +1,5 @@
import { atomWithHash } from "jotai/utils";
export const modalAtom = atomWithHash<
"cloudLogs" | "extensions" | "webhooks" | "export" | ""
>("modal", "");

View File

@@ -15,7 +15,8 @@ import CircularProgressOptical from "@src/components/CircularProgressOptical";
import { isTargetInsideBox } from "utils/fns";
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
import useBuildLogs from "./useBuildLogs";
import { modalAtom, cloudLogFiltersAtom } from "../utils";
import { modalAtom } from "@src/atoms/Table";
import { cloudLogFiltersAtom } from "../utils";
export interface IBuildLogsSnackProps {
onClose: () => void;

View File

@@ -189,6 +189,16 @@ export default function CloudLogItem({
<Typography variant="inherit" noWrap className="log-preview">
{data.payload === "textPayload" && data.textPayload}
{_get(data, "httpRequest.requestUrl")?.split(".run.app").pop()}
{data.payload === "jsonPayload" && (
<Typography
variant="inherit"
color="error"
fontWeight="bold"
component="span"
>
{data.jsonPayload.error}{" "}
</Typography>
)}
{data.payload === "jsonPayload" &&
stringify(data.jsonPayload.body ?? data.jsonPayload, {
space: 2,
@@ -203,16 +213,29 @@ export default function CloudLogItem({
</Typography>
)}
{data.payload === "jsonPayload" && (
<ReactJson
src={data.jsonPayload.body ?? data.jsonPayload}
name={data.jsonPayload.body ? "body" : "jsonPayload"}
theme={theme.palette.mode === "dark" ? "monokai" : "rjv-default"}
iconStyle="triangle"
style={{ font: "inherit", backgroundColor: "transparent" }}
displayDataTypes={false}
quotesOnKeys={false}
sortKeys
/>
<>
{data.payload === "jsonPayload" && data.jsonPayload.error && (
<Typography
variant="inherit"
color="error"
fontWeight="bold"
paragraph
style={{ whiteSpace: "pre-wrap" }}
>
{data.jsonPayload.error}
</Typography>
)}
<ReactJson
src={data.jsonPayload.body ?? data.jsonPayload}
name={data.jsonPayload.body ? "body" : "jsonPayload"}
theme={theme.palette.mode === "dark" ? "monokai" : "rjv-default"}
iconStyle="triangle"
style={{ font: "inherit", backgroundColor: "transparent" }}
displayDataTypes={false}
quotesOnKeys={false}
sortKeys
/>
</>
)}
{data.payload && <Divider sx={{ my: 1 }} />}

View File

@@ -4,11 +4,9 @@ import TableHeaderButton from "../TableHeaderButton";
import LogsIcon from "@src/assets/icons/CloudLogs";
import CloudLogsModal from "./CloudLogsModal";
import { modalAtom } from "./utils";
import { modalAtom } from "@src/atoms/Table";
export interface ICloudLogsProps {}
export default function CloudLogs(props: ICloudLogsProps) {
export default function CloudLogs() {
const [modal, setModal] = useAtom(modalAtom);
const open = modal === "cloudLogs";
const setOpen = (open: boolean) => setModal(open ? "cloudLogs" : "");

View File

@@ -3,8 +3,6 @@ import { sub } from "date-fns";
import type { IProjectContext } from "@src/contexts/ProjectContext";
export const modalAtom = atomWithHash<"cloudLogs" | "">("modal", "");
export type CloudLogFilters = {
type: "webhook" | "audit" | "build";
timeRange:

View File

@@ -1,4 +1,5 @@
import { useState, useMemo } from "react";
import { useAtom } from "jotai";
import { makeStyles, createStyles } from "@mui/styles";
import { DialogContent, Tab, Divider } from "@mui/material";
@@ -14,8 +15,9 @@ import ExportDetails from "./Export";
import DownloadDetails from "./Download";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { db } from "../../../firebase";
import { db } from "@src/firebase";
import { isCollectionGroup } from "@src/utils/fns";
import { modalAtom } from "@src/atoms/Table";
const useStyles = makeStyles((theme) =>
createStyles({
@@ -47,7 +49,11 @@ const useStyles = makeStyles((theme) =>
export default function Export() {
const classes = useStyles();
const [open, setOpen] = useState(false);
const [modal, setModal] = useAtom(modalAtom);
const open = modal === "export";
const setOpen = (open: boolean) => setModal(open ? "export" : "");
const [mode, setMode] = useState<"Export" | "Download">("Export");
const { tableState } = useProjectContext();

View File

@@ -1,4 +1,5 @@
import { useState } from "react";
import { useAtom } from "jotai";
import _isEqual from "lodash/isEqual";
import TableHeaderButton from "../TableHeaderButton";
@@ -17,6 +18,7 @@ import { useSnackLogContext } from "@src/contexts/SnackLogContext";
import { emptyExtensionObject, IExtension, ExtensionType } from "./utils";
import { runRoutes } from "@src/constants/runRoutes";
import { analytics } from "@src/analytics";
import { modalAtom } from "@src/atoms/Table";
export default function Extensions() {
const { tableState, tableActions, rowyRun } = useProjectContext();
@@ -28,7 +30,11 @@ export default function Extensions() {
const [localExtensionsObjects, setLocalExtensionsObjects] = useState(
currentExtensionObjects
);
const [openExtensionList, setOpenExtensionList] = useState(false);
const [modal, setModal] = useAtom(modalAtom);
const open = modal === "extensions";
const setOpen = (open: boolean) => setModal(open ? "extensions" : "");
const [openMigrationGuide, setOpenMigrationGuide] = useState(false);
const [extensionModal, setExtensionModal] = useState<{
mode: "add" | "update";
@@ -45,26 +51,26 @@ export default function Extensions() {
console.log("Extension migration required.");
setOpenMigrationGuide(true);
} else {
setOpenExtensionList(true);
setOpen(true);
}
};
const handleClose = (
setOpen: React.Dispatch<React.SetStateAction<boolean>>
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
if (edited) {
setOpen(true);
_setOpen(true);
requestConfirmation({
title: "Discard changes?",
confirm: "Discard",
handleConfirm: () => {
setOpen(false);
_setOpen(false);
setLocalExtensionsObjects(currentExtensionObjects);
setOpenExtensionList(false);
setOpen(false);
},
});
} else {
setOpenExtensionList(false);
setOpen(false);
}
};
@@ -73,7 +79,7 @@ export default function Extensions() {
"extensionObjects",
localExtensionsObjects
);
setOpenExtensionList(false);
setOpen(false);
};
const handleSaveDeploy = async () => {
@@ -189,7 +195,7 @@ export default function Extensions() {
icon={<ExtensionIcon />}
/>
{openExtensionList && !!tableState && (
{open && !!tableState && (
<Modal
onClose={handleClose}
disableBackdropClick={edited}
@@ -236,9 +242,7 @@ export default function Extensions() {
{extensionModal && (
<ExtensionModal
handleClose={() => {
setExtensionModal(null);
}}
handleClose={() => setExtensionModal(null)}
handleAdd={handleAddExtension}
handleUpdate={handleUpdateExtension}
mode={extensionModal.mode}
@@ -248,12 +252,10 @@ export default function Extensions() {
{openMigrationGuide && (
<ExtensionMigration
handleClose={() => {
setOpenMigrationGuide(false);
}}
handleClose={() => setOpenMigrationGuide(false)}
handleUpgradeComplete={() => {
setOpenMigrationGuide(false);
setOpenExtensionList(true);
setOpen(true);
}}
/>
)}

View File

@@ -22,10 +22,8 @@ import EmptyState from "@src/components/EmptyState";
import { webhookNames, IWebhook } from "./utils";
import { DATE_TIME_FORMAT } from "@src/constants/dates";
import { useProjectContext } from "@src/contexts/ProjectContext";
import {
modalAtom,
cloudLogFiltersAtom,
} from "@src/components/TableHeader/CloudLogs/utils";
import { modalAtom } from "@src/atoms/Table";
import { cloudLogFiltersAtom } from "@src/components/TableHeader/CloudLogs/utils";
export interface IWebhookListProps {
webhooks: IWebhook[];

View File

@@ -1,4 +1,5 @@
import { useState } from "react";
import { useAtom } from "jotai";
import _isEqual from "lodash/isEqual";
import TableHeaderButton from "../TableHeaderButton";
@@ -16,6 +17,7 @@ import { emptyWebhookObject, IWebhook, WebhookType } from "./utils";
import { runRoutes } from "@src/constants/runRoutes";
import { analytics } from "@src/analytics";
import { useSnackbar } from "notistack";
import { modalAtom } from "@src/atoms/Table";
export default function Webhooks() {
const { tableState, tableActions, rowyRun, compatibleRowyRunVersion } =
@@ -27,7 +29,11 @@ export default function Webhooks() {
const currentWebhooks = (tableState?.config.webhooks ?? []) as IWebhook[];
const [localWebhooksObjects, setLocalWebhooksObjects] =
useState(currentWebhooks);
const [openWebhookList, setOpenWebhookList] = useState(false);
const [modal, setModal] = useAtom(modalAtom);
const open = modal === "webhooks";
const setOpen = (open: boolean) => setModal(open ? "webhooks" : "");
const [webhookModal, setWebhookModal] = useState<{
mode: "add" | "update";
webhookObject: IWebhook;
@@ -38,32 +44,30 @@ export default function Webhooks() {
const edited = !_isEqual(currentWebhooks, localWebhooksObjects);
const handleOpen = () => {
setOpenWebhookList(true);
};
const handleOpen = () => setOpen(true);
const handleClose = (
setOpen: React.Dispatch<React.SetStateAction<boolean>>
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
if (edited) {
setOpen(true);
_setOpen(true);
requestConfirmation({
title: "Discard changes?",
confirm: "Discard",
handleConfirm: () => {
setOpen(false);
_setOpen(false);
setLocalWebhooksObjects(currentWebhooks);
setOpenWebhookList(false);
setOpen(false);
},
});
} else {
setOpenWebhookList(false);
setOpen(false);
}
};
const handleSaveWebhooks = async () => {
tableActions?.table.updateConfig("webhooks", localWebhooksObjects);
setOpenWebhookList(false);
setOpen(false);
// TODO: convert to async function that awaits for the document write to complete
await new Promise((resolve) => setTimeout(resolve, 500));
};
@@ -169,7 +173,7 @@ export default function Webhooks() {
icon={<WebhookIcon />}
/>
{openWebhookList && !!tableState && (
{open && !!tableState && (
<Modal
onClose={handleClose}
disableBackdropClick={edited}
@@ -215,9 +219,7 @@ export default function Webhooks() {
{webhookModal && (
<WebhookModal
handleClose={() => {
setWebhookModal(null);
}}
handleClose={() => setWebhookModal(null)}
handleAdd={handleAddWebhook}
handleUpdate={handleUpdateWebhook}
mode={webhookModal.mode}

View File

@@ -564,6 +564,12 @@ export const components = (theme: Theme): ThemeOptions => {
},
},
},
MuiLoadingButton: {
defaultProps: {
variant: "outlined",
color: "secondary",
},
},
MuiButtonGroup: {
styleOverrides: {
grouped: { minWidth: 32 },

View File

@@ -14,7 +14,9 @@ export default function PrivateRoute({ render, ...props }: IPrivateRouteProps) {
if (!!currentUser) return <Route {...props} render={render} />;
const redirect =
(props.location?.pathname ?? "") + (props.location?.search ?? "");
(props.location?.pathname ?? "") +
(props.location?.search ?? "") +
(props.location?.hash ?? "");
if (currentUser === null)
return (

View File

@@ -14568,9 +14568,9 @@ semver-diff@^3.1.1:
semver "^6.3.0"
semver-regex@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807"
integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA==
version "3.1.3"
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.3.tgz#b2bcc6f97f63269f286994e297e229b6245d0dc3"
integrity sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ==
"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1:
version "5.7.1"
@@ -15733,10 +15733,10 @@ tinycolor2@^1.4.1:
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
tinymce@^5.5.1, tinymce@^5.9.2:
version "5.9.2"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.9.2.tgz#c56a1d7800ac23026fbe6e0fcf444c0f157ccafe"
integrity sha512-/dHTsbxo0YwLvB5krRqiw/qHEm04/k6l0dvAQ3hO5oNw4e9QalKcUQCdr+g/b/FWcsUMP6scvKmm8MX50/j3Cg==
tinymce@^5.10.0, tinymce@^5.5.1:
version "5.10.0"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.10.0.tgz#5f88b40c2b26dc72b126f7d04944014dcb1ace05"
integrity sha512-SaqBK8GtTKYSsTfhKdN0+NrZRgmVWO+j3fvgzLjt0t/g0osNzRH5os8icm2Rv5HvaeTd4TpxetUuLE+R9yg/yg==
tmp@0.0.33, tmp@^0.0.33:
version "0.0.33"
@@ -15746,9 +15746,9 @@ tmp@0.0.33, tmp@^0.0.33:
os-tmpdir "~1.0.2"
tmpl@1.0.x:
version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
to-arraybuffer@^1.0.0:
version "1.0.1"