diff --git a/package.json b/package.json index 66b26e6..c2f98df 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@payloadcms/payload-cloud": "^3.35.1", "@payloadcms/richtext-lexical": "^3.35.1", "@payloadcms/storage-s3": "^3.35.1", + "country-state-city": "^3.2.1", "dayjs": "^1.11.13", "graphql": "^16.8.1", "next": "15.3.0", diff --git a/src/app/(main)/listings-for-rent/page.tsx b/src/app/(main)/listings-for-rent/page.tsx index 107775c..ead755e 100644 --- a/src/app/(main)/listings-for-rent/page.tsx +++ b/src/app/(main)/listings-for-rent/page.tsx @@ -3,6 +3,7 @@ import HeroImage from "@/components/HeroImage"; import Pagination from "@/components/Pagination"; import { CardPropertyData } from "@/schema/property"; import { getDefaultMetadata } from "@/utils/metadata"; +import { Country, State } from "country-state-city"; import { Metadata } from "next"; const metaDesc = "Explore the latest properties on the Dynamic Realty."; @@ -75,6 +76,9 @@ const propertiesData: CardPropertyData[] = [ ]; export default function ListingsForRent() { + // const countries = State.getStatesOfCountry("ID"); + // console.log(countries); + return ( <> diff --git a/src/collections/Properties.ts b/src/collections/Properties.ts new file mode 100644 index 0000000..e20de77 --- /dev/null +++ b/src/collections/Properties.ts @@ -0,0 +1,140 @@ +import formatSlug from "@/utils/payload/formatSlug"; +import type { CollectionConfig } from "payload"; + +export const Properties: CollectionConfig = { + slug: "properties", + labels: { plural: "Properties", singular: "Property" }, + fields: [ + { + name: "name", + type: "text", + required: true, + }, + { + name: "slug", + type: "text", + admin: { + position: "sidebar", + }, + hooks: { + beforeValidate: [formatSlug("name")], + }, + }, + { + name: "images", + type: "relationship", + relationTo: "media", + hasMany: true, + minRows: 1, + required: true, + }, + { + name: "aboutGroup", + label: "About", + type: "group", + fields: [ + { + name: "description", + type: "richText", + required: true, + }, + { + name: "area", + label: "Area (Sqft)", + type: "text", + required: true, + }, + { + name: "bathrooms_count", + label: "Total Bathrooms", + type: "text", + required: true, + }, + { + name: "bedrooms_count", + label: "Total Bedrooms", + type: "text", + required: true, + }, + ], + }, + { + name: "addressGroup", + label: "Address", + type: "group", + fields: [ + { + name: "country_code", + label: "Country", + type: "text", + required: true, + }, + { + name: "state_code", + label: "State", + type: "text", + required: true, + }, + { + name: "city_code", + label: "City", + type: "text", + required: true, + }, + { + name: "zip_code", + label: "Zip", + type: "text", + required: true, + }, + { + name: "address", + label: "Address", + type: "text", + required: true, + }, + ], + }, + { + name: "features", + type: "relationship", + relationTo: "propertyFeatures", + required: true, + hasMany: true, + minRows: 1, + }, + { + name: "base_price", + label: "Base Price", + type: "number", + required: true, + }, + { + name: "additional_price", + label: "Additional Price", + type: "array", + fields: [ + { + name: "name", + type: "text", + required: true, + }, + { + name: "price", + type: "number", + required: true, + }, + ], + }, + { + name: "embed_map_url", + label: "Embed Map URL", + type: "text", + }, + ], + admin: { + hideAPIURL: true, + group: "Properties", + useAsTitle: "name", + }, +}; diff --git a/src/collections/PropertyFeatures.ts b/src/collections/PropertyFeatures.ts new file mode 100644 index 0000000..2e3fef8 --- /dev/null +++ b/src/collections/PropertyFeatures.ts @@ -0,0 +1,18 @@ +import type { CollectionConfig } from "payload"; + +export const PropertyFeatures: CollectionConfig = { + slug: "propertyFeatures", + labels: { plural: "Property Features", singular: "Property Feature" }, + fields: [ + { + name: "name", + type: "text", + required: true, + }, + ], + admin: { + hideAPIURL: true, + group: "Properties", + useAsTitle: "name", + }, +}; diff --git a/src/payload-types.ts b/src/payload-types.ts index 6ea9a7e..c56a86d 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -72,6 +72,8 @@ export interface Config { blogTags: BlogTag; blogCategories: BlogCategory; blogs: Blog; + propertyFeatures: PropertyFeature; + properties: Property; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; @@ -83,6 +85,8 @@ export interface Config { blogTags: BlogTagsSelect | BlogTagsSelect; blogCategories: BlogCategoriesSelect | BlogCategoriesSelect; blogs: BlogsSelect | BlogsSelect; + propertyFeatures: PropertyFeaturesSelect | PropertyFeaturesSelect; + properties: PropertiesSelect | PropertiesSelect; 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; @@ -219,6 +223,65 @@ export interface Blog { createdAt: string; _status?: ('draft' | 'published') | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "propertyFeatures". + */ +export interface PropertyFeature { + id: number; + name: string; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "properties". + */ +export interface Property { + id: number; + name: string; + slug?: string | null; + images: (number | Media)[]; + aboutGroup: { + description: { + root: { + type: string; + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + }; + area: string; + bathrooms_count: string; + bedrooms_count: string; + }; + addressGroup: { + country_code: string; + state_code: string; + city_code: string; + zip_code: string; + address: string; + }; + features: (number | PropertyFeature)[]; + base_price: number; + additional_price?: + | { + name: string; + price: number; + id?: string | null; + }[] + | null; + embed_map_url?: string | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents". @@ -245,6 +308,14 @@ export interface PayloadLockedDocument { | ({ relationTo: 'blogs'; value: number | Blog; + } | null) + | ({ + relationTo: 'propertyFeatures'; + value: number | PropertyFeature; + } | null) + | ({ + relationTo: 'properties'; + value: number | Property; } | null); globalSlug?: string | null; user: { @@ -370,6 +441,53 @@ export interface BlogsSelect { createdAt?: T; _status?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "propertyFeatures_select". + */ +export interface PropertyFeaturesSelect { + name?: T; + updatedAt?: T; + createdAt?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "properties_select". + */ +export interface PropertiesSelect { + name?: T; + slug?: T; + images?: T; + aboutGroup?: + | T + | { + description?: T; + area?: T; + bathrooms_count?: T; + bedrooms_count?: T; + }; + addressGroup?: + | T + | { + country_code?: T; + state_code?: T; + city_code?: T; + zip_code?: T; + address?: T; + }; + features?: T; + base_price?: T; + additional_price?: + | T + | { + name?: T; + price?: T; + id?: T; + }; + embed_map_url?: T; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents_select". diff --git a/src/payload.config.ts b/src/payload.config.ts index 83618ec..9d87b61 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -13,6 +13,8 @@ import { Media } from "@/collections/Media"; import { BlogTags } from "@/collections/BlogTags"; import { BlogCategories } from "@/collections/BlogCategories"; import { Blogs } from "@/collections/Blogs"; +import { PropertyFeatures } from "@/collections/PropertyFeatures"; +import { Properties } from "./collections/Properties"; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -37,7 +39,7 @@ export default buildConfig({ }, theme: "dark", }, - collections: [Users, Media, BlogTags, BlogCategories, Blogs], + collections: [Users, Media, BlogTags, BlogCategories, Blogs, PropertyFeatures, Properties], editor: lexicalEditor(), secret: process.env.PAYLOAD_SECRET || "", typescript: { diff --git a/yarn.lock b/yarn.lock index 7579afd..e7a4fbd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4652,6 +4652,13 @@ __metadata: languageName: node linkType: hard +"country-state-city@npm:^3.2.1": + version: 3.2.1 + resolution: "country-state-city@npm:3.2.1" + checksum: 10c0/2545a000c207345514de31c20ed8a331bba9796f36ab1e6e4019ebb319bca37894180e4ade40eef9d7f2443aa60045ec3f317bfdb3f4c7b99b9c711e4688d8cb + languageName: node + linkType: hard + "croner@npm:9.0.0": version: 9.0.0 resolution: "croner@npm:9.0.0" @@ -5022,6 +5029,7 @@ __metadata: "@types/node": "npm:^20" "@types/react": "npm:^19" "@types/react-dom": "npm:^19" + country-state-city: "npm:^3.2.1" dayjs: "npm:^1.11.13" eslint: "npm:^9" eslint-config-next: "npm:15.3.0"