@lynxflow/seo-engine 1.7.6 → 1.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/connectors/wordpress/lynxseo-connector.zip +0 -0
- package/dist/google-business-profile.d.ts +19 -0
- package/dist/index.js +146 -0
- package/dist/index.mjs +146 -0
- package/package.json +1 -1
- package/src/google-business-profile.ts +182 -0
|
Binary file
|
|
@@ -15,6 +15,21 @@ export interface GoogleBusinessProfileConfig {
|
|
|
15
15
|
reviewCount?: number;
|
|
16
16
|
googleMapsUrl?: string;
|
|
17
17
|
}
|
|
18
|
+
export interface GbpAuditResult {
|
|
19
|
+
score: number;
|
|
20
|
+
grade: "A+" | "A" | "B" | "C" | "D";
|
|
21
|
+
passedChecks: number;
|
|
22
|
+
totalChecks: number;
|
|
23
|
+
checks: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
status: "pass" | "warn" | "fail";
|
|
26
|
+
points: number;
|
|
27
|
+
description: string;
|
|
28
|
+
recommendation?: string;
|
|
29
|
+
}>;
|
|
30
|
+
summary: string;
|
|
31
|
+
localPackEligibility: "Excellente" | "Moyenne" | "Faible";
|
|
32
|
+
}
|
|
18
33
|
export declare class GoogleBusinessProfileEngine {
|
|
19
34
|
/**
|
|
20
35
|
* Generates Schema.org LocalBusiness JSON-LD structure matching Google specifications
|
|
@@ -24,4 +39,8 @@ export declare class GoogleBusinessProfileEngine {
|
|
|
24
39
|
* Generates interactive HTML Badge with Google Review stars and direct Maps link
|
|
25
40
|
*/
|
|
26
41
|
static generateGbpBadgeHtml(config: GoogleBusinessProfileConfig): string;
|
|
42
|
+
/**
|
|
43
|
+
* Complete Diagnostic & Audit of a Google Business Profile / Local SEO setup
|
|
44
|
+
*/
|
|
45
|
+
static auditProfile(config: GoogleBusinessProfileConfig): GbpAuditResult;
|
|
27
46
|
}
|
package/dist/index.js
CHANGED
|
@@ -7145,6 +7145,152 @@ class GoogleBusinessProfileEngine {
|
|
|
7145
7145
|
</div>
|
|
7146
7146
|
`.trim();
|
|
7147
7147
|
}
|
|
7148
|
+
static auditProfile(config) {
|
|
7149
|
+
const checks = [];
|
|
7150
|
+
let score = 0;
|
|
7151
|
+
if (config.businessName && config.businessName.length >= 3) {
|
|
7152
|
+
checks.push({
|
|
7153
|
+
name: "Nom de l'établissement (NAP)",
|
|
7154
|
+
status: "pass",
|
|
7155
|
+
points: 15,
|
|
7156
|
+
description: `Nom correctement configuré : "${config.businessName}".`
|
|
7157
|
+
});
|
|
7158
|
+
score += 15;
|
|
7159
|
+
} else {
|
|
7160
|
+
checks.push({
|
|
7161
|
+
name: "Nom de l'établissement (NAP)",
|
|
7162
|
+
status: "fail",
|
|
7163
|
+
points: 15,
|
|
7164
|
+
description: "Nom d'établissement manquant ou trop court.",
|
|
7165
|
+
recommendation: "Renseignez le nom exact de votre entreprise tel qu'affiché sur Google Maps."
|
|
7166
|
+
});
|
|
7167
|
+
}
|
|
7168
|
+
if (config.streetAddress && config.postalCode && config.addressLocality) {
|
|
7169
|
+
checks.push({
|
|
7170
|
+
name: "Adresse physique complète",
|
|
7171
|
+
status: "pass",
|
|
7172
|
+
points: 20,
|
|
7173
|
+
description: `Adresse géolocalisée : ${config.streetAddress}, ${config.postalCode} ${config.addressLocality}.`
|
|
7174
|
+
});
|
|
7175
|
+
score += 20;
|
|
7176
|
+
} else {
|
|
7177
|
+
checks.push({
|
|
7178
|
+
name: "Adresse physique complète",
|
|
7179
|
+
status: "fail",
|
|
7180
|
+
points: 20,
|
|
7181
|
+
description: "Adresse ou code postal incomplet.",
|
|
7182
|
+
recommendation: "Renseignez la rue, le code postal et la ville pour le ciblage Google Maps Local Pack."
|
|
7183
|
+
});
|
|
7184
|
+
}
|
|
7185
|
+
if (config.telephone && config.telephone.length >= 8) {
|
|
7186
|
+
checks.push({
|
|
7187
|
+
name: "Téléphone direct vérifié",
|
|
7188
|
+
status: "pass",
|
|
7189
|
+
points: 10,
|
|
7190
|
+
description: `Numéro de contact : ${config.telephone}.`
|
|
7191
|
+
});
|
|
7192
|
+
score += 10;
|
|
7193
|
+
} else {
|
|
7194
|
+
checks.push({
|
|
7195
|
+
name: "Téléphone direct vérifié",
|
|
7196
|
+
status: "warn",
|
|
7197
|
+
points: 10,
|
|
7198
|
+
description: "Numéro de téléphone absent.",
|
|
7199
|
+
recommendation: "Ajoutez un numéro de téléphone fixe ou mobile local pour permettre les appels directs depuis les SERP."
|
|
7200
|
+
});
|
|
7201
|
+
}
|
|
7202
|
+
if (config.latitude && config.longitude && !isNaN(config.latitude) && !isNaN(config.longitude)) {
|
|
7203
|
+
checks.push({
|
|
7204
|
+
name: "Coordonnées GPS (Lat / Long)",
|
|
7205
|
+
status: "pass",
|
|
7206
|
+
points: 15,
|
|
7207
|
+
description: `Positionnement précis : ${config.latitude}, ${config.longitude}.`
|
|
7208
|
+
});
|
|
7209
|
+
score += 15;
|
|
7210
|
+
} else {
|
|
7211
|
+
checks.push({
|
|
7212
|
+
name: "Coordonnées GPS (Lat / Long)",
|
|
7213
|
+
status: "fail",
|
|
7214
|
+
points: 15,
|
|
7215
|
+
description: "Coordonnées GPS manquantes dans le schéma.",
|
|
7216
|
+
recommendation: "Ajoutez les coordonnées de latitude/longitude exactes pour optimiser l'affichage dans le Google 3-Pack."
|
|
7217
|
+
});
|
|
7218
|
+
}
|
|
7219
|
+
if (config.openingHours && config.openingHours.length > 0) {
|
|
7220
|
+
checks.push({
|
|
7221
|
+
name: "Horaires d'ouverture",
|
|
7222
|
+
status: "pass",
|
|
7223
|
+
points: 10,
|
|
7224
|
+
description: `${config.openingHours.length} créneau(x) d'ouverture configuré(s).`
|
|
7225
|
+
});
|
|
7226
|
+
score += 10;
|
|
7227
|
+
} else {
|
|
7228
|
+
checks.push({
|
|
7229
|
+
name: "Horaires d'ouverture",
|
|
7230
|
+
status: "warn",
|
|
7231
|
+
points: 10,
|
|
7232
|
+
description: "Horaires d'ouverture non spécifiés.",
|
|
7233
|
+
recommendation: "Indiquez vos horaires (ex: Mo-Fr 09:00-18:00) pour rassurer les clients et éviter le statut 'Fermé'."
|
|
7234
|
+
});
|
|
7235
|
+
}
|
|
7236
|
+
const rating = config.ratingValue || 0;
|
|
7237
|
+
const reviews = config.reviewCount || 0;
|
|
7238
|
+
if (rating >= 4.5 && reviews >= 20) {
|
|
7239
|
+
checks.push({
|
|
7240
|
+
name: "Réputation & Preuve Sociale",
|
|
7241
|
+
status: "pass",
|
|
7242
|
+
points: 20,
|
|
7243
|
+
description: `Excellente réputation : ${rating}/5 étoiles sur ${reviews} avis.`
|
|
7244
|
+
});
|
|
7245
|
+
score += 20;
|
|
7246
|
+
} else if (rating >= 4 && reviews > 0) {
|
|
7247
|
+
checks.push({
|
|
7248
|
+
name: "Réputation & Preuve Sociale",
|
|
7249
|
+
status: "warn",
|
|
7250
|
+
points: 10,
|
|
7251
|
+
description: `Note de ${rating}/5 avec ${reviews} avis.`,
|
|
7252
|
+
recommendation: "Augmentez le volume d'avis certifiés au-delà de 20 avis pour maximiser le taux de conversion."
|
|
7253
|
+
});
|
|
7254
|
+
score += 10;
|
|
7255
|
+
} else {
|
|
7256
|
+
checks.push({
|
|
7257
|
+
name: "Réputation & Preuve Sociale",
|
|
7258
|
+
status: "fail",
|
|
7259
|
+
points: 20,
|
|
7260
|
+
description: "Avis clients absents ou note inférieure à 4.0.",
|
|
7261
|
+
recommendation: "Activez la collecte d'avis clients réels pour afficher les étoiles dorées dans Google."
|
|
7262
|
+
});
|
|
7263
|
+
}
|
|
7264
|
+
if (config.placeId || config.googleMapsUrl) {
|
|
7265
|
+
checks.push({
|
|
7266
|
+
name: "Lien Google Place ID officiel",
|
|
7267
|
+
status: "pass",
|
|
7268
|
+
points: 10,
|
|
7269
|
+
description: "Lien direct vers la fiche Maps configuré."
|
|
7270
|
+
});
|
|
7271
|
+
score += 10;
|
|
7272
|
+
} else {
|
|
7273
|
+
checks.push({
|
|
7274
|
+
name: "Lien Google Place ID officiel",
|
|
7275
|
+
status: "warn",
|
|
7276
|
+
points: 10,
|
|
7277
|
+
description: "Place ID Google non renseigné.",
|
|
7278
|
+
recommendation: "Associez votre Place ID Google pour faciliter l'indexation par Google Maps."
|
|
7279
|
+
});
|
|
7280
|
+
}
|
|
7281
|
+
const passedChecks = checks.filter((c) => c.status === "pass").length;
|
|
7282
|
+
const grade = score >= 90 ? "A+" : score >= 75 ? "A" : score >= 60 ? "B" : score >= 40 ? "C" : "D";
|
|
7283
|
+
const eligibility = score >= 80 ? "Excellente" : score >= 50 ? "Moyenne" : "Faible";
|
|
7284
|
+
return {
|
|
7285
|
+
score,
|
|
7286
|
+
grade,
|
|
7287
|
+
passedChecks,
|
|
7288
|
+
totalChecks: checks.length,
|
|
7289
|
+
checks,
|
|
7290
|
+
summary: `Score Local SEO de ${score}/100 (Grade ${grade}) — Éligibilité Google Local 3-Pack : ${eligibility}.`,
|
|
7291
|
+
localPackEligibility: eligibility
|
|
7292
|
+
};
|
|
7293
|
+
}
|
|
7148
7294
|
}
|
|
7149
7295
|
// src/video-youtube-analyzer.ts
|
|
7150
7296
|
class VideoYouTubeAnalyzer {
|
package/dist/index.mjs
CHANGED
|
@@ -7031,6 +7031,152 @@ class GoogleBusinessProfileEngine {
|
|
|
7031
7031
|
</div>
|
|
7032
7032
|
`.trim();
|
|
7033
7033
|
}
|
|
7034
|
+
static auditProfile(config) {
|
|
7035
|
+
const checks = [];
|
|
7036
|
+
let score = 0;
|
|
7037
|
+
if (config.businessName && config.businessName.length >= 3) {
|
|
7038
|
+
checks.push({
|
|
7039
|
+
name: "Nom de l'établissement (NAP)",
|
|
7040
|
+
status: "pass",
|
|
7041
|
+
points: 15,
|
|
7042
|
+
description: `Nom correctement configuré : "${config.businessName}".`
|
|
7043
|
+
});
|
|
7044
|
+
score += 15;
|
|
7045
|
+
} else {
|
|
7046
|
+
checks.push({
|
|
7047
|
+
name: "Nom de l'établissement (NAP)",
|
|
7048
|
+
status: "fail",
|
|
7049
|
+
points: 15,
|
|
7050
|
+
description: "Nom d'établissement manquant ou trop court.",
|
|
7051
|
+
recommendation: "Renseignez le nom exact de votre entreprise tel qu'affiché sur Google Maps."
|
|
7052
|
+
});
|
|
7053
|
+
}
|
|
7054
|
+
if (config.streetAddress && config.postalCode && config.addressLocality) {
|
|
7055
|
+
checks.push({
|
|
7056
|
+
name: "Adresse physique complète",
|
|
7057
|
+
status: "pass",
|
|
7058
|
+
points: 20,
|
|
7059
|
+
description: `Adresse géolocalisée : ${config.streetAddress}, ${config.postalCode} ${config.addressLocality}.`
|
|
7060
|
+
});
|
|
7061
|
+
score += 20;
|
|
7062
|
+
} else {
|
|
7063
|
+
checks.push({
|
|
7064
|
+
name: "Adresse physique complète",
|
|
7065
|
+
status: "fail",
|
|
7066
|
+
points: 20,
|
|
7067
|
+
description: "Adresse ou code postal incomplet.",
|
|
7068
|
+
recommendation: "Renseignez la rue, le code postal et la ville pour le ciblage Google Maps Local Pack."
|
|
7069
|
+
});
|
|
7070
|
+
}
|
|
7071
|
+
if (config.telephone && config.telephone.length >= 8) {
|
|
7072
|
+
checks.push({
|
|
7073
|
+
name: "Téléphone direct vérifié",
|
|
7074
|
+
status: "pass",
|
|
7075
|
+
points: 10,
|
|
7076
|
+
description: `Numéro de contact : ${config.telephone}.`
|
|
7077
|
+
});
|
|
7078
|
+
score += 10;
|
|
7079
|
+
} else {
|
|
7080
|
+
checks.push({
|
|
7081
|
+
name: "Téléphone direct vérifié",
|
|
7082
|
+
status: "warn",
|
|
7083
|
+
points: 10,
|
|
7084
|
+
description: "Numéro de téléphone absent.",
|
|
7085
|
+
recommendation: "Ajoutez un numéro de téléphone fixe ou mobile local pour permettre les appels directs depuis les SERP."
|
|
7086
|
+
});
|
|
7087
|
+
}
|
|
7088
|
+
if (config.latitude && config.longitude && !isNaN(config.latitude) && !isNaN(config.longitude)) {
|
|
7089
|
+
checks.push({
|
|
7090
|
+
name: "Coordonnées GPS (Lat / Long)",
|
|
7091
|
+
status: "pass",
|
|
7092
|
+
points: 15,
|
|
7093
|
+
description: `Positionnement précis : ${config.latitude}, ${config.longitude}.`
|
|
7094
|
+
});
|
|
7095
|
+
score += 15;
|
|
7096
|
+
} else {
|
|
7097
|
+
checks.push({
|
|
7098
|
+
name: "Coordonnées GPS (Lat / Long)",
|
|
7099
|
+
status: "fail",
|
|
7100
|
+
points: 15,
|
|
7101
|
+
description: "Coordonnées GPS manquantes dans le schéma.",
|
|
7102
|
+
recommendation: "Ajoutez les coordonnées de latitude/longitude exactes pour optimiser l'affichage dans le Google 3-Pack."
|
|
7103
|
+
});
|
|
7104
|
+
}
|
|
7105
|
+
if (config.openingHours && config.openingHours.length > 0) {
|
|
7106
|
+
checks.push({
|
|
7107
|
+
name: "Horaires d'ouverture",
|
|
7108
|
+
status: "pass",
|
|
7109
|
+
points: 10,
|
|
7110
|
+
description: `${config.openingHours.length} créneau(x) d'ouverture configuré(s).`
|
|
7111
|
+
});
|
|
7112
|
+
score += 10;
|
|
7113
|
+
} else {
|
|
7114
|
+
checks.push({
|
|
7115
|
+
name: "Horaires d'ouverture",
|
|
7116
|
+
status: "warn",
|
|
7117
|
+
points: 10,
|
|
7118
|
+
description: "Horaires d'ouverture non spécifiés.",
|
|
7119
|
+
recommendation: "Indiquez vos horaires (ex: Mo-Fr 09:00-18:00) pour rassurer les clients et éviter le statut 'Fermé'."
|
|
7120
|
+
});
|
|
7121
|
+
}
|
|
7122
|
+
const rating = config.ratingValue || 0;
|
|
7123
|
+
const reviews = config.reviewCount || 0;
|
|
7124
|
+
if (rating >= 4.5 && reviews >= 20) {
|
|
7125
|
+
checks.push({
|
|
7126
|
+
name: "Réputation & Preuve Sociale",
|
|
7127
|
+
status: "pass",
|
|
7128
|
+
points: 20,
|
|
7129
|
+
description: `Excellente réputation : ${rating}/5 étoiles sur ${reviews} avis.`
|
|
7130
|
+
});
|
|
7131
|
+
score += 20;
|
|
7132
|
+
} else if (rating >= 4 && reviews > 0) {
|
|
7133
|
+
checks.push({
|
|
7134
|
+
name: "Réputation & Preuve Sociale",
|
|
7135
|
+
status: "warn",
|
|
7136
|
+
points: 10,
|
|
7137
|
+
description: `Note de ${rating}/5 avec ${reviews} avis.`,
|
|
7138
|
+
recommendation: "Augmentez le volume d'avis certifiés au-delà de 20 avis pour maximiser le taux de conversion."
|
|
7139
|
+
});
|
|
7140
|
+
score += 10;
|
|
7141
|
+
} else {
|
|
7142
|
+
checks.push({
|
|
7143
|
+
name: "Réputation & Preuve Sociale",
|
|
7144
|
+
status: "fail",
|
|
7145
|
+
points: 20,
|
|
7146
|
+
description: "Avis clients absents ou note inférieure à 4.0.",
|
|
7147
|
+
recommendation: "Activez la collecte d'avis clients réels pour afficher les étoiles dorées dans Google."
|
|
7148
|
+
});
|
|
7149
|
+
}
|
|
7150
|
+
if (config.placeId || config.googleMapsUrl) {
|
|
7151
|
+
checks.push({
|
|
7152
|
+
name: "Lien Google Place ID officiel",
|
|
7153
|
+
status: "pass",
|
|
7154
|
+
points: 10,
|
|
7155
|
+
description: "Lien direct vers la fiche Maps configuré."
|
|
7156
|
+
});
|
|
7157
|
+
score += 10;
|
|
7158
|
+
} else {
|
|
7159
|
+
checks.push({
|
|
7160
|
+
name: "Lien Google Place ID officiel",
|
|
7161
|
+
status: "warn",
|
|
7162
|
+
points: 10,
|
|
7163
|
+
description: "Place ID Google non renseigné.",
|
|
7164
|
+
recommendation: "Associez votre Place ID Google pour faciliter l'indexation par Google Maps."
|
|
7165
|
+
});
|
|
7166
|
+
}
|
|
7167
|
+
const passedChecks = checks.filter((c) => c.status === "pass").length;
|
|
7168
|
+
const grade = score >= 90 ? "A+" : score >= 75 ? "A" : score >= 60 ? "B" : score >= 40 ? "C" : "D";
|
|
7169
|
+
const eligibility = score >= 80 ? "Excellente" : score >= 50 ? "Moyenne" : "Faible";
|
|
7170
|
+
return {
|
|
7171
|
+
score,
|
|
7172
|
+
grade,
|
|
7173
|
+
passedChecks,
|
|
7174
|
+
totalChecks: checks.length,
|
|
7175
|
+
checks,
|
|
7176
|
+
summary: `Score Local SEO de ${score}/100 (Grade ${grade}) — Éligibilité Google Local 3-Pack : ${eligibility}.`,
|
|
7177
|
+
localPackEligibility: eligibility
|
|
7178
|
+
};
|
|
7179
|
+
}
|
|
7034
7180
|
}
|
|
7035
7181
|
// src/video-youtube-analyzer.ts
|
|
7036
7182
|
class VideoYouTubeAnalyzer {
|
package/package.json
CHANGED
|
@@ -16,6 +16,22 @@ export interface GoogleBusinessProfileConfig {
|
|
|
16
16
|
googleMapsUrl?: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export interface GbpAuditResult {
|
|
20
|
+
score: number;
|
|
21
|
+
grade: "A+" | "A" | "B" | "C" | "D";
|
|
22
|
+
passedChecks: number;
|
|
23
|
+
totalChecks: number;
|
|
24
|
+
checks: Array<{
|
|
25
|
+
name: string;
|
|
26
|
+
status: "pass" | "warn" | "fail";
|
|
27
|
+
points: number;
|
|
28
|
+
description: string;
|
|
29
|
+
recommendation?: string;
|
|
30
|
+
}>;
|
|
31
|
+
summary: string;
|
|
32
|
+
localPackEligibility: "Excellente" | "Moyenne" | "Faible";
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
export class GoogleBusinessProfileEngine {
|
|
20
36
|
/**
|
|
21
37
|
* Generates Schema.org LocalBusiness JSON-LD structure matching Google specifications
|
|
@@ -95,4 +111,170 @@ export class GoogleBusinessProfileEngine {
|
|
|
95
111
|
</div>
|
|
96
112
|
`.trim();
|
|
97
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Complete Diagnostic & Audit of a Google Business Profile / Local SEO setup
|
|
117
|
+
*/
|
|
118
|
+
static auditProfile(config: GoogleBusinessProfileConfig): GbpAuditResult {
|
|
119
|
+
const checks: GbpAuditResult["checks"] = [];
|
|
120
|
+
let score = 0;
|
|
121
|
+
|
|
122
|
+
// 1. Nom d'établissement
|
|
123
|
+
if (config.businessName && config.businessName.length >= 3) {
|
|
124
|
+
checks.push({
|
|
125
|
+
name: "Nom de l'établissement (NAP)",
|
|
126
|
+
status: "pass",
|
|
127
|
+
points: 15,
|
|
128
|
+
description: `Nom correctement configuré : "${config.businessName}".`
|
|
129
|
+
});
|
|
130
|
+
score += 15;
|
|
131
|
+
} else {
|
|
132
|
+
checks.push({
|
|
133
|
+
name: "Nom de l'établissement (NAP)",
|
|
134
|
+
status: "fail",
|
|
135
|
+
points: 15,
|
|
136
|
+
description: "Nom d'établissement manquant ou trop court.",
|
|
137
|
+
recommendation: "Renseignez le nom exact de votre entreprise tel qu'affiché sur Google Maps."
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 2. Adresse & Code Postal
|
|
142
|
+
if (config.streetAddress && config.postalCode && config.addressLocality) {
|
|
143
|
+
checks.push({
|
|
144
|
+
name: "Adresse physique complète",
|
|
145
|
+
status: "pass",
|
|
146
|
+
points: 20,
|
|
147
|
+
description: `Adresse géolocalisée : ${config.streetAddress}, ${config.postalCode} ${config.addressLocality}.`
|
|
148
|
+
});
|
|
149
|
+
score += 20;
|
|
150
|
+
} else {
|
|
151
|
+
checks.push({
|
|
152
|
+
name: "Adresse physique complète",
|
|
153
|
+
status: "fail",
|
|
154
|
+
points: 20,
|
|
155
|
+
description: "Adresse ou code postal incomplet.",
|
|
156
|
+
recommendation: "Renseignez la rue, le code postal et la ville pour le ciblage Google Maps Local Pack."
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 3. Téléphone
|
|
161
|
+
if (config.telephone && config.telephone.length >= 8) {
|
|
162
|
+
checks.push({
|
|
163
|
+
name: "Téléphone direct vérifié",
|
|
164
|
+
status: "pass",
|
|
165
|
+
points: 10,
|
|
166
|
+
description: `Numéro de contact : ${config.telephone}.`
|
|
167
|
+
});
|
|
168
|
+
score += 10;
|
|
169
|
+
} else {
|
|
170
|
+
checks.push({
|
|
171
|
+
name: "Téléphone direct vérifié",
|
|
172
|
+
status: "warn",
|
|
173
|
+
points: 10,
|
|
174
|
+
description: "Numéro de téléphone absent.",
|
|
175
|
+
recommendation: "Ajoutez un numéro de téléphone fixe ou mobile local pour permettre les appels directs depuis les SERP."
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 4. Coordonnées GPS
|
|
180
|
+
if (config.latitude && config.longitude && !isNaN(config.latitude) && !isNaN(config.longitude)) {
|
|
181
|
+
checks.push({
|
|
182
|
+
name: "Coordonnées GPS (Lat / Long)",
|
|
183
|
+
status: "pass",
|
|
184
|
+
points: 15,
|
|
185
|
+
description: `Positionnement précis : ${config.latitude}, ${config.longitude}.`
|
|
186
|
+
});
|
|
187
|
+
score += 15;
|
|
188
|
+
} else {
|
|
189
|
+
checks.push({
|
|
190
|
+
name: "Coordonnées GPS (Lat / Long)",
|
|
191
|
+
status: "fail",
|
|
192
|
+
points: 15,
|
|
193
|
+
description: "Coordonnées GPS manquantes dans le schéma.",
|
|
194
|
+
recommendation: "Ajoutez les coordonnées de latitude/longitude exactes pour optimiser l'affichage dans le Google 3-Pack."
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 5. Horaires d'ouverture
|
|
199
|
+
if (config.openingHours && config.openingHours.length > 0) {
|
|
200
|
+
checks.push({
|
|
201
|
+
name: "Horaires d'ouverture",
|
|
202
|
+
status: "pass",
|
|
203
|
+
points: 10,
|
|
204
|
+
description: `${config.openingHours.length} créneau(x) d'ouverture configuré(s).`
|
|
205
|
+
});
|
|
206
|
+
score += 10;
|
|
207
|
+
} else {
|
|
208
|
+
checks.push({
|
|
209
|
+
name: "Horaires d'ouverture",
|
|
210
|
+
status: "warn",
|
|
211
|
+
points: 10,
|
|
212
|
+
description: "Horaires d'ouverture non spécifiés.",
|
|
213
|
+
recommendation: "Indiquez vos horaires (ex: Mo-Fr 09:00-18:00) pour rassurer les clients et éviter le statut 'Fermé'."
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 6. Note Google & Volume d'Avis
|
|
218
|
+
const rating = config.ratingValue || 0;
|
|
219
|
+
const reviews = config.reviewCount || 0;
|
|
220
|
+
if (rating >= 4.5 && reviews >= 20) {
|
|
221
|
+
checks.push({
|
|
222
|
+
name: "Réputation & Preuve Sociale",
|
|
223
|
+
status: "pass",
|
|
224
|
+
points: 20,
|
|
225
|
+
description: `Excellente réputation : ${rating}/5 étoiles sur ${reviews} avis.`
|
|
226
|
+
});
|
|
227
|
+
score += 20;
|
|
228
|
+
} else if (rating >= 4.0 && reviews > 0) {
|
|
229
|
+
checks.push({
|
|
230
|
+
name: "Réputation & Preuve Sociale",
|
|
231
|
+
status: "warn",
|
|
232
|
+
points: 10,
|
|
233
|
+
description: `Note de ${rating}/5 avec ${reviews} avis.`,
|
|
234
|
+
recommendation: "Augmentez le volume d'avis certifiés au-delà de 20 avis pour maximiser le taux de conversion."
|
|
235
|
+
});
|
|
236
|
+
score += 10;
|
|
237
|
+
} else {
|
|
238
|
+
checks.push({
|
|
239
|
+
name: "Réputation & Preuve Sociale",
|
|
240
|
+
status: "fail",
|
|
241
|
+
points: 20,
|
|
242
|
+
description: "Avis clients absents ou note inférieure à 4.0.",
|
|
243
|
+
recommendation: "Activez la collecte d'avis clients réels pour afficher les étoiles dorées dans Google."
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 7. Lien Google Maps ou Place ID
|
|
248
|
+
if (config.placeId || config.googleMapsUrl) {
|
|
249
|
+
checks.push({
|
|
250
|
+
name: "Lien Google Place ID officiel",
|
|
251
|
+
status: "pass",
|
|
252
|
+
points: 10,
|
|
253
|
+
description: "Lien direct vers la fiche Maps configuré."
|
|
254
|
+
});
|
|
255
|
+
score += 10;
|
|
256
|
+
} else {
|
|
257
|
+
checks.push({
|
|
258
|
+
name: "Lien Google Place ID officiel",
|
|
259
|
+
status: "warn",
|
|
260
|
+
points: 10,
|
|
261
|
+
description: "Place ID Google non renseigné.",
|
|
262
|
+
recommendation: "Associez votre Place ID Google pour faciliter l'indexation par Google Maps."
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const passedChecks = checks.filter(c => c.status === "pass").length;
|
|
267
|
+
const grade: GbpAuditResult["grade"] = score >= 90 ? "A+" : score >= 75 ? "A" : score >= 60 ? "B" : score >= 40 ? "C" : "D";
|
|
268
|
+
const eligibility: GbpAuditResult["localPackEligibility"] = score >= 80 ? "Excellente" : score >= 50 ? "Moyenne" : "Faible";
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
score,
|
|
272
|
+
grade,
|
|
273
|
+
passedChecks,
|
|
274
|
+
totalChecks: checks.length,
|
|
275
|
+
checks,
|
|
276
|
+
summary: `Score Local SEO de ${score}/100 (Grade ${grade}) — Éligibilité Google Local 3-Pack : ${eligibility}.`,
|
|
277
|
+
localPackEligibility: eligibility
|
|
278
|
+
};
|
|
279
|
+
}
|
|
98
280
|
}
|