mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Merge branch 'develop' into rc
This commit is contained in:
@@ -202,9 +202,7 @@ export default function BulkActions({ selectedRows, columns, clearSelection }) {
|
||||
};
|
||||
const executeAction = async (key: string, actionType: string) => {
|
||||
const actionColumn = _find(actionColumns, { key });
|
||||
console.log(actionColumn);
|
||||
if (!actionColumn) return;
|
||||
console.log(actionColumn);
|
||||
if (actionColumn.config.isActionScript) {
|
||||
handleActionScript(actionColumn, actionType);
|
||||
} else {
|
||||
|
||||
@@ -82,8 +82,6 @@ export const cloudLogFetcher = (
|
||||
.join(encodeURIComponent("\n"))}`
|
||||
: "");
|
||||
|
||||
console.log(logQueryUrl);
|
||||
|
||||
if (rowyRun)
|
||||
return rowyRun<Record<string, any>[]>({
|
||||
route: { path: logQueryUrl, method: "GET" },
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function ImportCsvWizard({
|
||||
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
const { tableState, tableActions, addRow } = useProjectContext();
|
||||
const { tableState, tableActions, addRows } = useProjectContext();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const [config, setConfig] = useState<CsvConfig>({
|
||||
@@ -87,10 +87,10 @@ export default function ImportCsvWizard({
|
||||
}, [csvData, tableState, config]);
|
||||
|
||||
const handleFinish = () => {
|
||||
if (!tableState || !tableActions || !addRow || !parsedRows) return;
|
||||
if (!tableState || !tableActions || !addRows || !parsedRows) return;
|
||||
enqueueSnackbar("Importing data…");
|
||||
// Add all new rows — synchronous
|
||||
parsedRows!.forEach((newRow) => addRow(newRow, true));
|
||||
addRows(parsedRows.map((r) => ({ data: r })).reverse(), true);
|
||||
|
||||
// Add any new columns to the end
|
||||
for (const col of config.newColumns) {
|
||||
|
||||
@@ -96,7 +96,6 @@ export default function ActionFab({
|
||||
} else {
|
||||
result = await handleCallableAction(data);
|
||||
}
|
||||
console.log(result);
|
||||
const { message, success } = result;
|
||||
setIsRunning(false);
|
||||
enqueueSnackbar(JSON.stringify(message), {
|
||||
|
||||
@@ -141,7 +141,6 @@ export default function ConnectTableSelect({
|
||||
params: [algoliaIndex as string],
|
||||
});
|
||||
const { key } = resp;
|
||||
console.log(key);
|
||||
if (key) {
|
||||
const newKey = {
|
||||
key,
|
||||
|
||||
@@ -28,7 +28,6 @@ export const filterOperators: IFilterOperator[] = [
|
||||
];
|
||||
|
||||
export const valueFormatter = (value: any) => {
|
||||
console.log(value);
|
||||
if (value && value.toDate) {
|
||||
return value.toDate();
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ export const AppProvider: React.FC = ({ children }) => {
|
||||
const token: any = jwt_decode(authToken);
|
||||
if (token && token.exp * 1000 < Date.now()) {
|
||||
// token is expired
|
||||
console.log("token is expired,getting new token");
|
||||
const res = await currentUser.getIdTokenResult(true);
|
||||
setAuthToken(res.token as string);
|
||||
return res.token;
|
||||
@@ -125,7 +124,6 @@ export const AppProvider: React.FC = ({ children }) => {
|
||||
if (currentUser[curr]) return { ...acc, [curr]: currentUser[curr] };
|
||||
return acc;
|
||||
}, {});
|
||||
console.log("create user", userDoc.path, user);
|
||||
db.doc(userDoc.path).set({ user }, { merge: true });
|
||||
}
|
||||
}, [userDoc, currentUser]);
|
||||
|
||||
@@ -47,7 +47,15 @@ export interface IProjectContext {
|
||||
roles: string[];
|
||||
tableState: TableState;
|
||||
tableActions: TableActions;
|
||||
addRow: (data?: Record<string, any>, ignoreRequiredFields?: boolean) => void;
|
||||
addRow: (
|
||||
data?: Record<string, any>,
|
||||
ignoreRequiredFields?: boolean,
|
||||
id?: string
|
||||
) => void;
|
||||
addRows: (
|
||||
rows: { data?: Record<string, any>; id?: string }[],
|
||||
ignoreRequiredFields?: boolean
|
||||
) => void;
|
||||
deleteRow: (rowId) => void;
|
||||
updateCell: (
|
||||
ref: firebase.firestore.DocumentReference,
|
||||
@@ -225,6 +233,53 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
|
||||
return;
|
||||
};
|
||||
|
||||
const addRows = async (
|
||||
rows: { data?: any; id?: string }[],
|
||||
ignoreRequiredFields?: boolean
|
||||
) => {
|
||||
const valuesFromFilter = tableState.filters.reduce((acc, curr) => {
|
||||
if (curr.operator === "==") {
|
||||
return { ...acc, [curr.key]: curr.value };
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
}, {});
|
||||
const initialData = Object.values(tableState.columns).reduce(
|
||||
(acc, column) => {
|
||||
if (column.config?.defaultValue?.type === "static") {
|
||||
return { ...acc, [column.key]: column.config.defaultValue.value };
|
||||
} else if (column.config?.defaultValue?.type === "null") {
|
||||
return { ...acc, [column.key]: null };
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const requiredFields = Object.values(tableState.columns)
|
||||
.filter((column) => column.config.required)
|
||||
.map((column) => column.key);
|
||||
|
||||
if (table?.audit !== false) {
|
||||
initialData[table?.auditFieldCreatedBy || "_createdBy"] = rowyUser(
|
||||
currentUser!
|
||||
);
|
||||
initialData[table?.auditFieldUpdatedBy || "_updatedBy"] = rowyUser(
|
||||
currentUser!
|
||||
);
|
||||
}
|
||||
|
||||
await tableActions.addRows(
|
||||
rows.map((row) => ({
|
||||
data: { ...valuesFromFilter, ...initialData, ...row.data },
|
||||
})),
|
||||
ignoreRequiredFields ? [] : requiredFields,
|
||||
(rowId: string) => auditChange("ADD_ROW", rowId, {})
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
const updateCell: IProjectContext["updateCell"] = (
|
||||
ref,
|
||||
fieldName,
|
||||
@@ -330,6 +385,7 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
|
||||
tableState,
|
||||
tableActions,
|
||||
addRow,
|
||||
addRows,
|
||||
updateCell,
|
||||
deleteRow,
|
||||
settingsActions,
|
||||
|
||||
@@ -10,6 +10,7 @@ export type TableActions = {
|
||||
update: Function;
|
||||
reorder: Function;
|
||||
};
|
||||
addRows: Function;
|
||||
row: { add: Function; delete: Function; more: Function; update: Function };
|
||||
table: {
|
||||
set: (id: string, collection: string, filters: TableFilter[]) => void;
|
||||
@@ -94,6 +95,7 @@ export default function useTable() {
|
||||
remove: configActions.remove,
|
||||
reorder: configActions.reorder,
|
||||
},
|
||||
addRows: tableActions.addRows,
|
||||
row: {
|
||||
add: tableActions.addRow,
|
||||
update: tableActions.updateRow,
|
||||
|
||||
@@ -13,8 +13,8 @@ import { useAppContext } from "@src/contexts/AppContext";
|
||||
import { TableFilter, TableOrder } from ".";
|
||||
import {
|
||||
isCollectionGroup,
|
||||
generateSmallerId,
|
||||
missingFieldsReducer,
|
||||
decrementId,
|
||||
} from "@src/utils/fns";
|
||||
|
||||
// Safety parameter sets the upper limit of number of docs fetched by this hook
|
||||
@@ -99,7 +99,6 @@ const useTableData = () => {
|
||||
tableInitialState
|
||||
);
|
||||
const [rows, rowsDispatch] = useReducer(rowsReducer, []);
|
||||
|
||||
/** set collection listener
|
||||
* @param filters
|
||||
* @param limit max number of docs
|
||||
@@ -252,7 +251,6 @@ const useTableData = () => {
|
||||
return rowsDispatch({ type: "delete", rowId });
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
if (error.code === "permission-denied") {
|
||||
enqueueSnackbar("You do not have the permissions to delete this row.", {
|
||||
variant: "error",
|
||||
@@ -282,21 +280,22 @@ const useTableData = () => {
|
||||
const addRow = async (
|
||||
data: any,
|
||||
requiredFields: string[],
|
||||
onSuccess: (rowId: string) => void
|
||||
onSuccess: (rowId: string) => void,
|
||||
id?: string
|
||||
) => {
|
||||
const missingRequiredFields = requiredFields
|
||||
? requiredFields.reduce(missingFieldsReducer(data), [])
|
||||
: [];
|
||||
|
||||
const { path } = tableState;
|
||||
let rowIndex = 0;
|
||||
let seedId = rows[rowIndex]?.id ?? "zzzzzzzzzzzzzzzzzzzz";
|
||||
while (seedId.split("").every((char) => char === "0")) {
|
||||
rowIndex += 1;
|
||||
seedId = rows[rowIndex].id;
|
||||
}
|
||||
const newId = generateSmallerId(seedId);
|
||||
|
||||
const newId =
|
||||
id ??
|
||||
decrementId(
|
||||
rows[0]?.id ?? "zzzzzzzzzzzzzzzzzzzzzzzz",
|
||||
Math.round(Math.random() * 100)
|
||||
);
|
||||
//generateSmallerId(rows[0]?.id ?? "zzzzzzzzzzzzzzzzzzzzzzzz");
|
||||
if (missingRequiredFields.length === 0) {
|
||||
try {
|
||||
await db
|
||||
@@ -327,6 +326,57 @@ const useTableData = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** creating new rows from array
|
||||
* @param rows
|
||||
* @param onSuccess
|
||||
* @param requiredFields
|
||||
*/
|
||||
const addRows = async (
|
||||
rows: { data: any; id?: string }[],
|
||||
requiredFields: string[],
|
||||
onSuccess: (rowIds: string) => void
|
||||
) => {
|
||||
let previousId = rows[0]?.id ?? "zzzzzzzzzzzzzzzzzzzzzzzz";
|
||||
rows.forEach(async (row) => {
|
||||
const { data, id } = row;
|
||||
const missingRequiredFields = requiredFields
|
||||
? requiredFields.reduce(missingFieldsReducer(data), [])
|
||||
: [];
|
||||
const newId =
|
||||
id ?? decrementId(previousId, Math.round(Math.random() * 100));
|
||||
previousId = newId;
|
||||
if (missingRequiredFields.length === 0) {
|
||||
try {
|
||||
await db
|
||||
.collection(tableState.path)
|
||||
.doc(newId)
|
||||
.set(data, { merge: true })
|
||||
.then(() => {
|
||||
onSuccess(newId);
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.code === "permission-denied") {
|
||||
enqueueSnackbar(
|
||||
"You do not have the permissions to add new rows.",
|
||||
{
|
||||
variant: "error",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const ref = db.collection(tableState.path).doc(newId);
|
||||
const newRow = {
|
||||
...data,
|
||||
id: newId,
|
||||
ref,
|
||||
_missingRequiredFields: missingRequiredFields,
|
||||
};
|
||||
rowsDispatch({ type: "add", newRow });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateRow = (rowRef, update, onSuccess, onError) => {
|
||||
rowsDispatch({ type: "update", update, rowRef, onSuccess, onError });
|
||||
};
|
||||
@@ -350,6 +400,7 @@ const useTableData = () => {
|
||||
deleteRow,
|
||||
setTable,
|
||||
addRow,
|
||||
addRows,
|
||||
updateRow,
|
||||
moreRows,
|
||||
dispatch: tableDispatch,
|
||||
|
||||
116
src/utils/fns.ts
116
src/utils/fns.ts
@@ -84,6 +84,104 @@ export const makeId = (length: number = 20) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
function convertBase(str, fromBase, toBase) {
|
||||
const add = (x, y, base: number) => {
|
||||
let z: number[] = [];
|
||||
const n = Math.max(x.length, y.length);
|
||||
let carry = 0;
|
||||
let i = 0;
|
||||
while (i < n || carry) {
|
||||
const xi = i < x.length ? x[i] : 0;
|
||||
const yi = i < y.length ? y[i] : 0;
|
||||
const zi = carry + xi + yi;
|
||||
z.push(zi % base);
|
||||
carry = Math.floor(zi / base);
|
||||
i++;
|
||||
}
|
||||
return z;
|
||||
};
|
||||
|
||||
const multiplyByNumber = (num, x, base) => {
|
||||
if (num < 0) return null;
|
||||
if (num == 0) return [];
|
||||
|
||||
let result: number[] = [];
|
||||
let power = x;
|
||||
while (true) {
|
||||
num & 1 && (result = add(result, power, base));
|
||||
num = num >> 1;
|
||||
if (num === 0) break;
|
||||
power = add(power, power, base);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const parseToDigitsArray = (str, base) => {
|
||||
const digits = str.split("");
|
||||
let arr: number[] = [];
|
||||
for (let i = digits.length - 1; i >= 0; i--) {
|
||||
const n = characters.indexOf(digits[i]);
|
||||
if (n == -1) return "0";
|
||||
arr.push(n);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
const digits = parseToDigitsArray(str, fromBase);
|
||||
if (digits === null) return "0";
|
||||
|
||||
let outArray: number[] = [];
|
||||
let power: number[] | null = [1];
|
||||
for (let i = 0; i < digits.length; i++) {
|
||||
digits[i] &&
|
||||
(outArray = add(
|
||||
outArray,
|
||||
multiplyByNumber(digits[i], power, toBase),
|
||||
toBase
|
||||
));
|
||||
power = multiplyByNumber(fromBase, power, toBase);
|
||||
}
|
||||
|
||||
let out = "";
|
||||
for (let i = outArray.length - 1; i >= 0; i--) out += characters[outArray[i]];
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export const decrementId = (id, dec = 1) => {
|
||||
let newId = id.split("");
|
||||
const trailingZeros: string[] = [];
|
||||
const leadingZeros: string[] = [];
|
||||
const leadingId: string[] = [];
|
||||
while (newId[0] == "0") {
|
||||
leadingZeros.push(newId.shift());
|
||||
}
|
||||
// remove all the zeros
|
||||
while (newId[newId.length - 1] == "0") {
|
||||
trailingZeros.push(newId.pop());
|
||||
}
|
||||
// put back at most 6 zeros
|
||||
newId = newId.concat(trailingZeros.splice(0, 6));
|
||||
while (newId.length > 8) {
|
||||
leadingId.push(newId.shift());
|
||||
}
|
||||
const currentIndex: string | null = convertBase(newId.join(""), 62, 10);
|
||||
|
||||
if (currentIndex === null) throw new Error("Could not convert id to number");
|
||||
|
||||
if (parseInt(currentIndex) < 1 || Number.isNaN(parseInt(currentIndex)))
|
||||
return `${id}${convertBase(`${Math.random() * 10000}`, 10, 62)}`;
|
||||
console.log({ id, val: parseInt(currentIndex) });
|
||||
const newIndex = parseInt(currentIndex) - dec;
|
||||
|
||||
return `${leadingZeros.join("")}${leadingId.join("")}${convertBase(
|
||||
`${newIndex}`,
|
||||
10,
|
||||
62
|
||||
)}${trailingZeros.join("")}`;
|
||||
};
|
||||
|
||||
export const generateSmallerId = (id: string) => {
|
||||
const generated = id.split("");
|
||||
for (let i = generated.length - 1; i >= 0; i--) {
|
||||
@@ -106,24 +204,6 @@ export const generateSmallerId = (id: string) => {
|
||||
return generated.join("");
|
||||
};
|
||||
|
||||
export const generateBiggerId = (id: string) => {
|
||||
const generated = id.split("");
|
||||
for (let i = generated.length - 1; i >= 0; i--) {
|
||||
const charIndex = characters.indexOf(id[i]);
|
||||
console.log(i, id[i], charIndex);
|
||||
if (charIndex < characters.length - 1) {
|
||||
generated[i] = characters[charIndex + 1];
|
||||
break;
|
||||
} else if (i > 0) {
|
||||
continue;
|
||||
} else {
|
||||
generated.push(characters[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return generated.join("");
|
||||
};
|
||||
|
||||
// Gets sub-table ID in $1
|
||||
const formatPathRegex = /\/[^\/]+\/([^\/]+)/g;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user