Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staging #261

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/client/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_GOOGLE_PLACES_API=GIzaSyBYZUIAKIEP_kboIuNb0SK4KzlAmJc-YWo
8 changes: 8 additions & 0 deletions app/client/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{
<<<<<<< HEAD
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
}
}
=======
"extends": [
"next/core-web-vitals",
"next/typescript"
]
}
>>>>>>> pr/Birdmannn/257
14 changes: 14 additions & 0 deletions app/client/components.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
<<<<<<< HEAD
"style": "new-york",
=======
"style": "default",
>>>>>>> pr/Birdmannn/257
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
<<<<<<< HEAD
"baseColor": "zinc",
"cssVariables": true,
=======
"baseColor": "neutral",
"cssVariables": false,
>>>>>>> pr/Birdmannn/257
"prefix": ""
},
"aliases": {
Expand All @@ -16,5 +25,10 @@
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
<<<<<<< HEAD
},
"iconLibrary": "lucide"
=======
}
>>>>>>> pr/Birdmannn/257
}
99 changes: 99 additions & 0 deletions app/client/components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Component Library Documentation

This PR provides a set of reusable components designed for common UI tasks. Below are the details for each component and how to use them effectively.

---

## Components Overview

### 1. **`P` Component**
The `P` component is used for headings and paragraphs.

#### Props:
- **`size`**: Controls the size of the text.
- **`weight`**: Sets the font weight.
- **`classname`**: Allows custom styles to be added.
- **`align`**: Aligns the text (e.g., left, center, right).

Refer to the component code for the expected values for these props.

---

### 2. **`Input TextField` Component**
This component is a customizable input field with extended functionality.

#### Props:
- **HTML Input Attributes**: All standard input attributes (e.g., `type`, `placeholder`, etc.).
- **`error`**: *(Optional)* A string displayed below the field to indicate validation errors.
- **`onChange`**: A callback function to handle and receive the input field's value.
- **`classname`**: Allows custom styles to be added.

---

### 3. **`Input SelectField` Component**
This component is used for rendering dropdowns.

#### Props:
- **HTML Select Attributes**: Standard select element attributes.
- **`options`**: An array of objects defining the dropdown's content.
- **`nameKey`**: Specifies the object property to display in the dropdown.
- **`value`**: Sets the default selected value.
- **`onChange`**: A callback function to handle the selected value in the parent component.
- **`classname`**: Allows custom styles to be added.

---

### 4. **`Modal` Component**
A flexible modal for displaying content overlays.

#### Props:
- **`isOpen`**: A boolean controlling whether the modal is visible.
- **`onClose`**: A callback function to handle modal closure.
- **`size`**: Specifies the modal size. Options include:
- `sm` (small)
- `md` (medium)
- `lg` (large)

---

### 5. **`Button` Component**
A versatile button component with customizable styles.

#### Props:
- **HTML Button Attributes**: All standard button attributes.
- **`variant`**: Controls the button's color.
- **`size`**: Adjusts the button's size.
- **`type`**: Specifies the button type (e.g., `submit`, `button`).
- **`outlined`**: A boolean determining whether the button has an outlined style.
- **`onClick`**: A callback function triggered when the button is clicked.
- **`classname`**: Allows custom styles to be added.

---

### 6. **`Table` Component**
This component renders a data table with filtering and searching capabilities.

#### Props:
- **`columns`**: Defines the table's column structure. See [`./src/app/components/Table/DefaultColumn.tsx`](./src/app/components/Table/DefaultColumn.tsx) for an example.
- **`data`**: The dataset to display. Each row's properties must match the column configuration.
- **`filterData`**: An array of strings for filtering table data.
- **`searchBy`**: A string specifying the column to filter the table using the search field.
- **`filterColumn`**: Specifies the column used for filtering with `filterData`.

---

### 7. **`PlacesAutocomplete` Component**
A location search input field powered by the Google Places API.

#### Props:
- **`label`**: The label for the input field.
- **`onLocationSelect`**: A callback function returning the selected location's:
- Latitude
- Longitude
- Address

---

## Usage Examples

Refer to the individual component files for usage examples and implementation details. These components are designed to be flexible, reusable, and easy to integrate into your projects.
17 changes: 17 additions & 0 deletions app/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,33 @@
"lint": "next lint"
},
"dependencies": {
<<<<<<< HEAD
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@react-google-maps/api": "^2.20.3",
"@starknet-react/chains": "^3.1.0",
"@starknet-react/core": "^3.6.0",
"@tanstack/react-table": "^8.20.5",
"class-variance-authority": "^0.7.0",
=======
"@radix-ui/react-slot": "^1.1.0",
"@starknet-react/chains": "^3.1.0",
"@starknet-react/core": "^3.6.0",
"blockies-ts": "^1.0.0",
"class-variance-authority": "^0.7.1",
>>>>>>> pr/Birdmannn/257
"clsx": "^2.1.1",
"lucide-react": "^0.460.0",
"next": "14.2.15",
"react": "^18",
"react-calendar": "^5.1.0",
"react-date-range": "^2.0.1",
"react-dom": "^18",
<<<<<<< HEAD
"starknet": "^6.11.0",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"use-places-autocomplete": "^4.0.1"
=======
"react-icons": "^5.3.0",
"react-spinners": "^0.14.1",
"react-toastify": "^10.0.6",
Expand All @@ -29,8 +44,10 @@
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"zustand": "^5.0.1"
>>>>>>> pr/Birdmannn/257
},
"devDependencies": {
"@types/google.maps": "^3.58.1",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-date-range": "^1.4.9",
Expand Down
6 changes: 6 additions & 0 deletions app/client/public/icons/layer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions app/client/src/app/components/Input/PlacesAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from "react";
import { useLoadScript } from "@react-google-maps/api";
import usePlacesAutocomplete, {
getGeocode,
getLatLng,
} from "use-places-autocomplete";
import TextInput from "./TextField";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

interface GooglePlaceInputProps {
label?: string;
onLocationSelect: (lat: number, lng: number, address: string) => void;
}

const PlacesAutocomplete: React.FC<GooglePlaceInputProps> = ({
label = "Location",
onLocationSelect,
}) => {
const [error, setError] = useState<string | null>(null);
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: process.env.NEXT_GOOGLE_PLACES_API as string,
libraries: ["places"],
});
const {
ready,
value,
setValue,
suggestions: { status, data },
clearSuggestions,
} = usePlacesAutocomplete({
// requestOptions: {
// Specify additional options like bounds or types if needed
// componentRestrictions: { country: "us" },
// },
debounce: 300,
});

const handleInputChange = (value: string) => {
setValue(value);
};

const handleSelect = async (address: string) => {
setValue(address, false);
clearSuggestions();

try {
const results = await getGeocode({ address });
const { lat, lng } = await getLatLng(results[0]);
onLocationSelect(lat, lng, address);
setError(null);
} catch (error) {
console.error("Error fetching location details: ", error);
setError("Failed to get location details. Please try again.");
}
};

if (loadError) return <div>Error loading Google Maps script</div>;
if (!isLoaded) return <div>Loading Component...</div>
return (
<div className="w-full">
{label && (
<label className="block text-sm font-medium mb-2">{label}</label>
)}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<TextInput
type="text"
value={value}
onChange={handleInputChange}
placeholder="Enter a location"
disabled={!ready}
/>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-full max-w-md">
{status === "OK" &&
data.map(({ place_id, description }) => (
<DropdownMenuItem
key={place_id}
onClick={() => handleSelect(description)}>
{description}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
{error && <p className="text-sm text-red-500 mt-2">{error}</p>}
</div>
);
};

export default PlacesAutocomplete;
78 changes: 78 additions & 0 deletions app/client/src/app/components/Input/SelectField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useEffect, useState } from "react";
import { ChevronDown } from "lucide-react";
import Paragraph from "../P/P";

interface SelectProps<T>
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "onChange"> {
options: T[];
nameKey: keyof T;
label?: string;
error?: string;
classname?: string;
onChange?: (value: string) => void;
value?: string | undefined;
}

const SelectField = <T,>({
options,
nameKey,
label,
error,
classname = "",
onChange,
value,
...props
}: SelectProps<T>) => {
const [isOpen, setIsOpen] = useState(false);
const [selected, setSelected] = useState<string | null | undefined>(null);

const toggleDropdown = () => setIsOpen(!isOpen);

const handleOptionClick = (value: string) => {
setSelected(value);
setIsOpen(false);
onChange?.(value);
};

useEffect(() => {
setSelected(value);
}, [value])

return (
<div className={`cursor-pointer relative w-full ${classname}`}>
<ChevronDown
color="#828282"
size={20}
className={`absolute right-2 ${label ? 'top-10' : 'top-2.5'} pointer-events-none cursor-pointer`}
/>
{label && (
<Paragraph classname="block text-sm font-medium text-black mb-1">
{label}
{props.required && <span className="text-red ml-0.5">*</span>}
</Paragraph>
)}
<div
onClick={toggleDropdown}
className={`w-full px-3.5 py-2.5 min-h-11 border appearance-none rounded-md focus:outline-none text-grey-2 placeholder:text-grey focus:ring-1 focus:ring-primary focus:border-primary ${
error ? "border-error" : "border-grey-4"
}`}>
{selected || ""}
</div>
{isOpen && (
<div className="absolute mt-2 w-full bg-white shadow-lg rounded-lg border border-grey-4 z-10 max-h-[165px] overflow-auto">
{options.map((option, index) => (
<div
key={index}
className="px-3.5 py-4 cursor-pointer text-grey-2 hover:bg-secondary hover:text-primary"
onClick={() => handleOptionClick(String(option[nameKey]))}>
{String(option[nameKey])}
</div>
))}
</div>
)}
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
</div>
);
};

export default SelectField;
Loading
Loading