diff --git a/README.md b/README.md
index d6b82b7..b1886ba 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,9 @@ Displays a searchable directory of offices using the Bridge Data Output API.
- **Search Functionality:** Users can search offices by name, phone, or email.
- **Customizable Display:** Adjust the number of columns and rows in the block editor.
- **Cache Management:** Clear the cache manually if needed.
+- **Enhanced Directory Display:** Offices are displayed as a grid of cards for better visual presentation.
+- **Live Search with Debounce:** Users can search offices with instant feedback, and the search input is debounced to optimize performance.
+- **Infinite Scroll:** As users scroll down, more offices are loaded automatically without needing to click pagination links.
## Data Storage
diff --git a/bridge-directory/assets/css/bridge-directory.css b/bridge-directory/assets/css/bridge-directory.css
new file mode 100644
index 0000000..8761c83
--- /dev/null
+++ b/bridge-directory/assets/css/bridge-directory.css
@@ -0,0 +1,37 @@
+.bridge-directory-grid {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+.bridge-directory-search {
+ margin-bottom: 20px;
+}
+
+.bridge-directory-search input {
+ width: 100%;
+ padding: 10px;
+ font-size: 16px;
+}
+
+.bridge-directory-cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 20px;
+}
+
+.bridge-directory-card {
+ border: 1px solid #ccc;
+ padding: 15px;
+ background-color: #fff;
+}
+
+.bridge-directory-card h3 {
+ margin-top: 0;
+}
+
+.bridge-directory-loader {
+ text-align: center;
+ padding: 20px;
+ font-size: 18px;
+}
diff --git a/bridge-directory/assets/js/bridge-directory.js b/bridge-directory/assets/js/bridge-directory.js
new file mode 100644
index 0000000..31221a3
--- /dev/null
+++ b/bridge-directory/assets/js/bridge-directory.js
@@ -0,0 +1,86 @@
+(function ($) {
+ let currentPage = 1;
+ let isLoading = false;
+ let noMoreResults = false;
+ let lastQuery = '';
+ let debounceTimeout;
+
+ $(document).ready(function () {
+ const $searchInput = $('#bridge-directory-search-input');
+ const $cardsContainer = $('#bridge-directory-cards');
+ const $loader = $('#bridge-directory-loader');
+
+ function loadResults(reset = false) {
+ if (isLoading || noMoreResults) return;
+ isLoading = true;
+ $loader.show();
+
+ $.ajax({
+ url: bridgeDirectory.ajax_url,
+ type: 'POST',
+ data: {
+ action: 'bridge_directory_load_offices',
+ nonce: bridgeDirectory.nonce,
+ page: currentPage,
+ query: lastQuery,
+ },
+ success: function (response) {
+ if (reset) {
+ $cardsContainer.empty();
+ currentPage = 1;
+ noMoreResults = false;
+ }
+ if (response.success) {
+ const offices = response.data.offices;
+ if (offices.length === 0) {
+ noMoreResults = true;
+ } else {
+ appendResultsToGrid(offices);
+ currentPage++;
+ }
+ }
+ },
+ complete: function () {
+ isLoading = false;
+ $loader.hide();
+ },
+ });
+ }
+
+ function appendResultsToGrid(offices) {
+ offices.forEach(function (office) {
+ const card = `
+
+
${office.OfficeName}
+
Phone: ${office.OfficePhone || ''}
+
Email: ${office.OfficeEmail || ''}
+
Address: ${office.OfficeAddress1 || ''} ${office.OfficeAddress2 || ''}, ${office.OfficeCity || ''}, ${office.OfficeStateOrProvince || ''} ${office.OfficePostalCode || ''}
+ ${office.SocialMediaWebsiteUrlOrId ? `
Website: ${office.SocialMediaWebsiteUrlOrId}
` : ''}
+
+ `;
+ $cardsContainer.append(card);
+ });
+ }
+
+ function debounceSearch() {
+ clearTimeout(debounceTimeout);
+ debounceTimeout = setTimeout(function () {
+ lastQuery = $searchInput.val();
+ currentPage = 1;
+ noMoreResults = false;
+ loadResults(true);
+ }, 300);
+ }
+
+ $searchInput.on('input', debounceSearch);
+
+ $(window).on('scroll', function () {
+ if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
+ loadResults();
+ }
+ });
+
+ // Initial load
+ loadResults();
+ });
+})(jQuery);
diff --git a/bridge-directory/bridge-directory.php b/bridge-directory/bridge-directory.php
index a05b6c1..b834c4a 100644
--- a/bridge-directory/bridge-directory.php
+++ b/bridge-directory/bridge-directory.php
@@ -39,6 +39,9 @@
$data_sync = new Data_Sync( $db_handler );
$data_sync->schedule_incremental_sync();
+// Initialize AJAX Handler
+$ajax_handler = new AJAX_Handler( $search_handler );
+
// Activation and Deactivation Hooks
register_activation_hook( __FILE__, [ 'BridgeDirectory\DB_Handler', 'activate' ] );
register_deactivation_hook( __FILE__, [ 'BridgeDirectory\DB_Handler', 'deactivate' ] );
@@ -56,4 +59,3 @@ function bridge_directory_custom_cron_schedule( $schedules ) {
];
return $schedules;
}
-
\ No newline at end of file
diff --git a/bridge-directory/includes/class-ajax-handler.php b/bridge-directory/includes/class-ajax-handler.php
new file mode 100644
index 0000000..0d82bec
--- /dev/null
+++ b/bridge-directory/includes/class-ajax-handler.php
@@ -0,0 +1,27 @@
+search_handler = $search_handler;
+
+ add_action( 'wp_ajax_bridge_directory_load_offices', [ $this, 'load_offices' ] );
+ add_action( 'wp_ajax_nopriv_bridge_directory_load_offices', [ $this, 'load_offices' ] );
+ }
+
+ public function load_offices() {
+ check_ajax_referer( 'bridge_directory_nonce', 'nonce' );
+
+ $page = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
+ $query = isset( $_POST['query'] ) ? sanitize_text_field( $_POST['query'] ) : '';
+ $limit = 20; // Number of results per page
+
+ $offices = $this->search_handler->search_offices( $query, $page, $limit );
+
+ wp_send_json_success( [ 'offices' => $offices ] );
+ }
+}
diff --git a/bridge-directory/includes/class-block-register.php b/bridge-directory/includes/class-block-register.php
index c626121..cb12d36 100644
--- a/bridge-directory/includes/class-block-register.php
+++ b/bridge-directory/includes/class-block-register.php
@@ -12,6 +12,7 @@ public function __construct( $search_handler ) {
public function register() {
add_action( 'init', [ $this, 'register_blocks' ] );
+ add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
}
public function register_blocks() {
@@ -30,52 +31,50 @@ public function register_blocks() {
'type' => 'number',
'default' => 3,
],
- 'rows' => [
- 'type' => 'number',
- 'default' => 5,
- ],
],
'render_callback' => [ $this, 'render_block' ],
] );
}
- public function render_block( $attributes ) {
- $query = isset( $_GET['bridge_search'] ) ? sanitize_text_field( $_GET['bridge_search'] ) : '';
- $offices = $this->search_handler->search_offices( $query );
+ public function enqueue_scripts() {
+ if ( has_block( 'bridge-directory/office-list' ) ) {
+ wp_enqueue_script(
+ 'bridge-directory-frontend',
+ plugins_url( 'assets/js/bridge-directory.js', __DIR__ ),
+ [ 'jquery' ],
+ '1.0.0',
+ true
+ );
- ob_start();
- ?>
+ wp_enqueue_style(
+ 'bridge-directory-style',
+ plugins_url( 'assets/css/bridge-directory.css', __DIR__ ),
+ [],
+ '1.0.0'
+ );
-
+ wp_localize_script( 'bridge-directory-frontend', 'bridgeDirectory', [
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
+ 'columns' => get_option( 'bridge_directory_columns', 3 ),
+ 'nonce' => wp_create_nonce( 'bridge_directory_nonce' ),
+ ] );
+ }
+ }
-
- = $attributes['rows'] * $attributes['columns'] ) {
- break;
- }
- ?>
-
-
-
Phone:
-
Email:
-
Address:
-
-
Website:
-
-
-
+ public function render_block( $attributes ) {
+ ob_start();
+ ?>
+
+
+
+
+
+
+
+
+ Loading...
+
-
db_handler = $db_handler;
}
- public function search_offices( $query ) {
+ public function search_offices( $query = '', $page = 1, $limit = 20 ) {
global $wpdb;
+ $offset = ( $page - 1 ) * $limit;
$table_name = $this->db_handler->table_name;
$sql = "SELECT * FROM {$table_name}";
+ $where_clauses = [];
if ( ! empty( $query ) ) {
$like_query = '%' . $wpdb->esc_like( $query ) . '%';
- $sql .= $wpdb->prepare(
- " WHERE OfficeName LIKE %s OR OfficePhone LIKE %s OR OfficeEmail LIKE %s",
+ $where_clauses[] = $wpdb->prepare(
+ "(OfficeName LIKE %s OR OfficePhone LIKE %s OR OfficeEmail LIKE %s)",
$like_query,
$like_query,
$like_query
);
}
+ if ( ! empty( $where_clauses ) ) {
+ $sql .= ' WHERE ' . implode( ' AND ', $where_clauses );
+ }
+
+ $sql .= $wpdb->prepare( ' LIMIT %d OFFSET %d', $limit, $offset );
+
$results = $wpdb->get_results( $sql, ARRAY_A );
return $results;
}
diff --git a/bridge-directory/vendor/composer/installed.php b/bridge-directory/vendor/composer/installed.php
index 99aa7db..7eb3e2a 100644
--- a/bridge-directory/vendor/composer/installed.php
+++ b/bridge-directory/vendor/composer/installed.php
@@ -1,20 +1,20 @@
array(
- 'name' => '__root__',
- 'pretty_version' => '1.0.0+no-version-set',
- 'version' => '1.0.0.0',
- 'reference' => null,
- 'type' => 'library',
+ 'name' => 'justinh-rahb/bridge-directory',
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => '48aaf176c17a698321aeff4ea84371fcbab1577b',
+ 'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
- '__root__' => array(
- 'pretty_version' => '1.0.0+no-version-set',
- 'version' => '1.0.0.0',
- 'reference' => null,
- 'type' => 'library',
+ 'justinh-rahb/bridge-directory' => array(
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => '48aaf176c17a698321aeff4ea84371fcbab1577b',
+ 'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,