Function bodies 45 total
fixFirestore function · javascript · L24-L107 (84 LOC)scripts/fix_firestore.js
async function fixFirestore() {
console.log('🔥 Connecting to Firestore Project:', process.env.VITE_FIREBASE_PROJECT_ID);
console.log('Scanning "business" collection for missing city/state...');
try {
const snapshot = await db.collection('business').get();
if (snapshot.empty) {
console.log('No businesses found.');
return;
}
console.log(`Found ${snapshot.size} documents. Processing...`);
let updatedCount = 0;
let batch = db.batch();
let batchCount = 0;
const BATCH_SIZE = 400; // Limit is 500, playing safe
for (const doc of snapshot.docs) {
const b = doc.data();
let needsUpdate = false;
let updates = {};
// Check if city/state is missing or clearly invalid (like numbers "15", "10" from previous bad script)
// Or just check if we can parse better data
// We only look at those with address
if (b.saveGooglePlaceToDb function · javascript · L2816-L2975 (160 LOC)server/index_legacy.js
async function saveGooglePlaceToDb(place, defaultCity = '', defaultState = '') {
const businessRef = db.collection('business');
// 1. Parse Data FIRST (so we can update existing records if needed)
let category = 'Outros';
const types = place.types || [];
if (types.includes('restaurant') || types.includes('food') || types.includes('meal_takeaway') || types.includes('bar') || types.includes('cafe')) category = 'Alimentação';
else if (types.includes('bakery')) category = 'Padaria';
else if (types.includes('pharmacy') || types.includes('drugstore')) category = 'Farmácia';
else if (types.includes('health') || types.includes('doctor') || types.includes('hospital') || types.includes('dentist')) category = 'Saúde';
else if (types.includes('gym')) category = 'Academia';
else if (types.includes('supermarket') || types.includes('grocery_or_supermarket') || types.includes('convenience_store')) category = 'Mercado';
else if (types.includes('shopping_mallmigrate function · javascript · L29-L82 (54 LOC)server/scripts/migrate-country-code.js
async function migrate() {
console.log('Starting migration: Add country_code to businesses...\n');
const businessesRef = db.collection('businesses');
const snapshot = await businessesRef.get();
let updated = 0;
let skipped = 0;
let errors = 0;
let batch = db.batch();
let batchCount = 0;
for (const doc of snapshot.docs) {
const data = doc.data();
// Skip if already has country_code
if (data.country_code) {
skipped++;
continue;
}
try {
batch.update(doc.ref, {
country_code: 'BR',
country: data.country || 'Brasil',
});
updated++;
batchCount++;
// Firestore batches have a 500 operation limit
if (batchCount >= 499) {
await batch.commit();
console.log(` Committed batch of ${batchCount} updates...`);
batch = db.batch();
batchCount = 0;
}
} catch (err) {
console.error(` Error updating doc ${doc.id}:`, err.message);
errors++;
}
seed function · javascript · L52-L76 (25 LOC)server/seed.js
async function seed() {
console.log('🌱 Seeding database...');
for (const business of businesses) {
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(business)
});
const data = await response.json();
if (response.ok) {
console.log(`✅ Created: ${business.name}`);
} else {
console.error(`❌ Failed to create ${business.name}:`, data);
}
} catch (error) {
console.error(`❌ Error creating ${business.name}:`, error.message);
}
}
console.log('✨ Seeding complete!');
}CollectionReference class · javascript · L26-L110 (85 LOC)server/src/config/firebase_mock.js
class CollectionReference {
constructor(name) {
this.name = name;
this.filters = [];
this.limitVal = null;
this.orderByVal = null;
}
doc(id) {
return new DocumentReference(this.name, id);
}
where(field, op, value) {
this.filters.push({ field, op, value });
return this;
}
limit(n) {
this.limitVal = n;
return this;
}
orderBy(field, dir) {
this.orderByVal = { field, dir };
return this;
}
async get() {
await db.read();
let data = db.data[this.name] || [];
// Apply filters
for (const filter of this.filters) {
if (filter.op === '==') {
data = data.filter(item => item[filter.field] === filter.value);
}
}
// Apply OrderBy
if (this.orderByVal) {
data.sort((a, b) => {
const valA = a[this.orderByVal.field];
const valB = b[constructor method · javascript · L27-L32 (6 LOC)server/src/config/firebase_mock.js
constructor(name) {
this.name = name;
this.filters = [];
this.limitVal = null;
this.orderByVal = null;
}doc method · javascript · L34-L36 (3 LOC)server/src/config/firebase_mock.js
doc(id) {
return new DocumentReference(this.name, id);
}Repobility · code-quality intelligence platform · https://repobility.com
where method · javascript · L38-L41 (4 LOC)server/src/config/firebase_mock.js
where(field, op, value) {
this.filters.push({ field, op, value });
return this;
}limit method · javascript · L43-L46 (4 LOC)server/src/config/firebase_mock.js
limit(n) {
this.limitVal = n;
return this;
}orderBy method · javascript · L48-L51 (4 LOC)server/src/config/firebase_mock.js
orderBy(field, dir) {
this.orderByVal = { field, dir };
return this;
}get method · javascript · L53-L94 (42 LOC)server/src/config/firebase_mock.js
async get() {
await db.read();
let data = db.data[this.name] || [];
// Apply filters
for (const filter of this.filters) {
if (filter.op === '==') {
data = data.filter(item => item[filter.field] === filter.value);
}
}
// Apply OrderBy
if (this.orderByVal) {
data.sort((a, b) => {
const valA = a[this.orderByVal.field];
const valB = b[this.orderByVal.field];
if (this.orderByVal.dir === 'desc') return valB - valA;
return valA - valB;
});
}
// Apply Limit
if (this.limitVal) {
data = data.slice(0, this.limitVal);
}
const docs = data.map(item => {
const idField = ID_FIELDS[this.name] || 'id';
return {
id: item[idField],
data: () => item,
ref: new DocumentReference(this.name, item[idFadd method · javascript · L95-L109 (15 LOC)server/src/config/firebase_mock.js
async add(data) {
await db.read();
const collection = db.data[this.name] || [];
const idField = ID_FIELDS[this.name] || 'id';
// Generate random ID if not present (simple mock)
const newId = Math.random().toString(36).substring(2, 15);
const dataWithId = { ...data, [idField]: newId };
collection.push(dataWithId);
db.data[this.name] = collection;
await db.write();
return new DocumentReference(this.name, newId);
}DocumentReference class · javascript · L112-L183 (72 LOC)server/src/config/firebase_mock.js
class DocumentReference {
constructor(collectionName, id) {
this.collectionName = collectionName;
this.id = id;
}
getIdField() {
return ID_FIELDS[this.collectionName] || 'id';
}
async set(data) {
await db.read();
const collection = db.data[this.collectionName] || [];
const idField = this.getIdField();
const index = collection.findIndex(item => item[idField] === this.id);
// Ensure ID is in data
const dataWithId = { ...data, [idField]: this.id };
if (index > -1) {
collection[index] = dataWithId;
} else {
collection.push(dataWithId);
}
db.data[this.collectionName] = collection;
await db.write();
}
async get() {
await db.read();
const collection = db.data[this.collectionName] || [];
const idField = this.getIdField();
const item = collection.find(item => item[idField] === this.id);
rconstructor method · javascript · L113-L116 (4 LOC)server/src/config/firebase_mock.js
constructor(collectionName, id) {
this.collectionName = collectionName;
this.id = id;
}getIdField method · javascript · L118-L120 (3 LOC)server/src/config/firebase_mock.js
getIdField() {
return ID_FIELDS[this.collectionName] || 'id';
}Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
set method · javascript · L122-L138 (17 LOC)server/src/config/firebase_mock.js
async set(data) {
await db.read();
const collection = db.data[this.collectionName] || [];
const idField = this.getIdField();
const index = collection.findIndex(item => item[idField] === this.id);
// Ensure ID is in data
const dataWithId = { ...data, [idField]: this.id };
if (index > -1) {
collection[index] = dataWithId;
} else {
collection.push(dataWithId);
}
db.data[this.collectionName] = collection;
await db.write();
}get method · javascript · L140-L151 (12 LOC)server/src/config/firebase_mock.js
async get() {
await db.read();
const collection = db.data[this.collectionName] || [];
const idField = this.getIdField();
const item = collection.find(item => item[idField] === this.id);
return {
exists: !!item,
data: () => item,
ref: this
};
}update method · javascript · L153-L167 (15 LOC)server/src/config/firebase_mock.js
async update(data) {
await db.read();
const collection = db.data[this.collectionName] || [];
const idField = this.getIdField();
const index = collection.findIndex(item => item[idField] === this.id);
if (index > -1) {
collection[index] = { ...collection[index], ...data };
db.data[this.collectionName] = collection;
await db.write();
} else {
// Firestore update fails if doc doesn't exist, but for mock we can ignore or throw
console.warn(`Document ${this.id} not found in ${this.collectionName} for update`);
}
}handleClickOutside function · typescript · L59-L63 (5 LOC)src/components/CitySearch.tsx
function handleClickOutside(event: MouseEvent) {
if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) {
setShowSuggestions(false);
}
}ErrorBoundary class · typescript · L13-L67 (55 LOC)src/components/ErrorBoundary.tsx
export class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
error: null
};
public static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
if (import.meta.env.DEV) console.error('Uncaught error:', error, errorInfo);
}
public render() {
if (this.state.hasError) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center p-4">
<div className="bg-white rounded-xl shadow-lg p-8 max-w-md w-full text-center">
<div className="bg-red-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<AlertTriangle className="w-8 h-8 text-red-600" />
</div>
<h1 className="text-2xl font-bold text-gray-90getDerivedStateFromError method · typescript · L19-L21 (3 LOC)src/components/ErrorBoundary.tsx
public static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}componentDidCatch method · typescript · L23-L25 (3 LOC)src/components/ErrorBoundary.tsx
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
if (import.meta.env.DEV) console.error('Uncaught error:', error, errorInfo);
}render method · typescript · L27-L66 (40 LOC)src/components/ErrorBoundary.tsx
public render() {
if (this.state.hasError) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center p-4">
<div className="bg-white rounded-xl shadow-lg p-8 max-w-md w-full text-center">
<div className="bg-red-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<AlertTriangle className="w-8 h-8 text-red-600" />
</div>
<h1 className="text-2xl font-bold text-gray-900 mb-2">Ops! Algo deu errado.</h1>
<p className="text-gray-600 mb-6">
Desculpe, encontramos um erro inesperado. Tente recarregar a página.
</p>
{this.state.error && (
<div className="bg-gray-100 p-3 rounded text-left text-xs text-gray-500 font-mono mb-6 overflow-auto max-h-32">
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
RequireAdminAuth function · typescript · L4-L13 (10 LOC)src/components/RequireAdminAuth.tsx
export default function RequireAdminAuth({ children }: { children: React.ReactNode }) {
const token = localStorage.getItem('admin_token');
const location = useLocation();
if (!token) {
return <Navigate to="/admin/login" state={{ from: location }} replace />;
}
return <>{children}</>;
}useInactivityTracker function · typescript · L10-L40 (31 LOC)src/hooks/useInactivityTracker.ts
export function useInactivityTracker(timeoutMs = 10000): boolean {
const [isInactive, setIsInactive] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
const resetInactivity = () => {
setIsInactive(false);
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
setIsInactive(true);
}, timeoutMs);
};
// Listeners
window.addEventListener('mousemove', resetInactivity);
window.addEventListener('keydown', resetInactivity);
window.addEventListener('click', resetInactivity);
// Init
resetInactivity();
return () => {
window.removeEventListener('mousemove', resetInactivity);
window.removeEventListener('keydown', resetInactivity);
window.removeEventListener('click', resetInactivity);
if (timerRef.current) cleagetLocaleFromCountry function · typescript · L44-L46 (3 LOC)src/i18n/countryLocaleMap.ts
export function getLocaleFromCountry(countryCode: string): string {
return countryLocaleMap[countryCode.toUpperCase()] || 'en';
}cn function · typescript · L4-L6 (3 LOC)src/lib/utils.ts
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}formatWhatsAppLink function · typescript · L8-L29 (22 LOC)src/lib/utils.ts
export function formatWhatsAppLink(phone: string | undefined | null): string | null {
if (!phone) return null;
// Remove all non-numeric characters
const cleaned = phone.replace(/\D/g, '');
if (!cleaned) return null;
// If it starts with 55 and is long enough (12-13 digits), assume it's already formatted
if (cleaned.startsWith('55') && cleaned.length >= 12) {
return `https://wa.me/${cleaned}`;
}
// If it's a standard Brazilian number (10-11 digits), prepend 55
if (cleaned.length >= 10 && cleaned.length <= 11) {
return `https://wa.me/55${cleaned}`;
}
// Fallback: return as is if it doesn't match known patterns but has digits
// This handles cases where it might be an international number without +
return `https://wa.me/${cleaned}`;
}getGreeting function · typescript · L37-L42 (6 LOC)src/lib/utils.ts
function getGreeting(): string {
const hour = new Date().getHours();
if (hour >= 5 && hour < 12) return GREETINGS.morning;
if (hour >= 12 && hour < 18) return GREETINGS.afternoon;
return GREETINGS.night;
}createWhatsAppMessageLink function · typescript · L74-L139 (66 LOC)src/lib/utils.ts
export function createWhatsAppMessageLink(
phone: string | undefined | null,
businessName: string,
businessId: string,
userName?: string | null,
cityName?: string | null
): string | null {
const formattedPhone = formatWhatsAppLink(phone);
if (!formattedPhone) return null;
const name = userName || "Visitante";
const city = cityName || "Uberaba"; // Default fallback per request hints or keep generic? User said "cidade" variable. If null, safe fallback.
const greeting = getGreeting();
const linkTaaberto = `https://www.taaberto.com.br/empresa/${businessId}`; // Updated base URL as per request: https://www.taaberto.com.br
// Logic to pick a random template avoiding the immediate last one
// Note: This runs on client side, so localStorage is available.
// However, during SSR or non-browser envs this might fail. We should check process/window.
let templateIndex = 0;
if (typeof window !== 'undefined') {
const lastIndexKAdminLogin function · typescript · L5-L117 (113 LOC)src/pages/admin/AdminLogin.tsx
export default function AdminLogin() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);
try {
// reCAPTCHA Removed per user request
// Use relative URL for local development (proxy) or production same-domain
const API_URL = ''; // Was 'https://opennow-...'
const res = await fetch(`${API_URL}/api/admin/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'omit',
body: JSON.stringify({ email, password })
});
const contentType = res.headers.get("content-type");
if (!contentType || !contentType.If a scraper extracted this row, it came from Repobility (https://repobility.com)
handleClickOutside function · typescript · L49-L53 (5 LOC)src/pages/RegisterBusiness.tsx
function handleClickOutside(event: MouseEvent) {
if (locationWrapperRef.current && !locationWrapperRef.current.contains(event.target as Node)) {
setShowLocationDropdown(false);
}
}ApiError class · typescript · L5-L12 (8 LOC)src/services/apiClient.ts
export class ApiError extends Error {
status: number;
constructor(status: number, message: string) {
super(message);
this.name = 'ApiError';
this.status = status;
}
}constructor method · typescript · L7-L11 (5 LOC)src/services/apiClient.ts
constructor(status: number, message: string) {
super(message);
this.name = 'ApiError';
this.status = status;
}getAuthToken function · typescript · L19-L23 (5 LOC)src/services/apiClient.ts
async function getAuthToken(): Promise<string | null> {
const user = auth.currentUser;
if (!user) return null;
return user.getIdToken();
}apiClient function · typescript · L25-L57 (33 LOC)src/services/apiClient.ts
export async function apiClient<T = unknown>(endpoint: string, options: ApiOptions = {}): Promise<T> {
const { requireAuth = false, body, headers: customHeaders, ...rest } = options;
const headers: Record<string, string> = {
...customHeaders as Record<string, string>,
};
if (body) {
headers['Content-Type'] = 'application/json';
}
if (requireAuth) {
const token = await getAuthToken();
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
}
const response = await fetch(`${API_BASE}${endpoint}`, {
...rest,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
const errorText = await response.text().catch(() => response.statusText);
throw new ApiError(response.status, errorText);
}
const text = await response.text();
if (!text) return undefined as T;
return JSON.parse(text) as T;
}WebSocketService class · typescript · L3-L96 (94 LOC)src/services/WebSocketService.ts
class WebSocketService {
private ws: WebSocket | null = null;
private subscribers = new Map<string, Set<MessageHandler>>();
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
private reconnectDelay = 3000;
private url: string = '';
private connected = false;
connect(): void {
if (this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING) return;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.host;
this.url = `${protocol}//${host}`;
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.connected = true;
this.reconnectDelay = 3000;
};
this.ws.onmessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
const type = data.type as string;
// Notify type-specific subscribers
this.subscribers.get(type)?.forEach(handler => handlerconnect method · typescript · L11-L52 (42 LOC)src/services/WebSocketService.ts
connect(): void {
if (this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING) return;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.host;
this.url = `${protocol}//${host}`;
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.connected = true;
this.reconnectDelay = 3000;
};
this.ws.onmessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
const type = data.type as string;
// Notify type-specific subscribers
this.subscribers.get(type)?.forEach(handler => handler(data));
// Notify wildcard subscribers
this.subscribers.get('*')?.forEach(handler => handler(data));
} catch {
// Ignore malformed messages
}
};
this.ws.onclose = () => {
this.connected = false;
this.ws = null;
scheduleReconnect method · typescript · L54-L61 (8 LOC)src/services/WebSocketService.ts
private scheduleReconnect(): void {
if (this.reconnectTimer) return;
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null;
this.connect();
}, this.reconnectDelay);
this.reconnectDelay = Math.min(this.reconnectDelay * 1.5, 30000);
}Repobility · code-quality intelligence platform · https://repobility.com
subscribe method · typescript · L63-L75 (13 LOC)src/services/WebSocketService.ts
subscribe(messageType: string, handler: MessageHandler): () => void {
if (!this.subscribers.has(messageType)) {
this.subscribers.set(messageType, new Set());
}
this.subscribers.get(messageType)!.add(handler);
return () => {
this.subscribers.get(messageType)?.delete(handler);
if (this.subscribers.get(messageType)?.size === 0) {
this.subscribers.delete(messageType);
}
};
}send method · typescript · L77-L81 (5 LOC)src/services/WebSocketService.ts
send(data: unknown): void {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}disconnect method · typescript · L83-L91 (9 LOC)src/services/WebSocketService.ts
disconnect(): void {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
this.ws?.close();
this.ws = null;
this.connected = false;
}verifyRecentImports function · javascript · L13-L34 (22 LOC)verify_import.cjs
async function verifyRecentImports() {
console.log("Fetching last 5 businesses...");
const snapshot = await db.collection('business')
.orderBy('created_at', 'desc')
.limit(5)
.get();
if (snapshot.empty) {
console.log("No businesses found.");
return;
}
snapshot.forEach(doc => {
const data = doc.data();
console.log("---------------------------------------------------");
console.log(`Name: ${data.name}`);
console.log(`Location: ${data.city} - ${data.state}, ${data.country || 'N/A'}`);
console.log(`Phone Field: ${data.phone || '(empty)'}`);
console.log(`WhatsApp Field: ${data.whatsapp || '(empty)'}`);
console.log(`Created At: ${new Date(data.created_at).toLocaleString()}`);
});
}parseAddressOld function · javascript · L8-L21 (14 LOC)verify_parsing.js
function parseAddressOld(formatted_address) {
const parts = formatted_address.split(',').map(p => p.trim());
const lastPart = parts[parts.length - 1];
let parsedCity = '';
let parsedState = '';
let parsedCountry = lastPart;
if (parts.length >= 3) {
parsedCity = parts[parts.length - 3];
parsedState = parts[parts.length - 2];
}
return { city: parsedCity, state: parsedState, country: parsedCountry };
}parseAddressNew function · javascript · L23-L63 (41 LOC)verify_parsing.js
function parseAddressNew(formatted_address) {
const parts = formatted_address.split(',').map(p => p.trim());
const ufs = ['AC', 'AL', 'AP', 'AM', 'BA', 'CE', 'DF', 'ES', 'GO', 'MA', 'MT', 'MS', 'MG', 'PA', 'PB', 'PR', 'PE', 'PI', 'RJ', 'RN', 'RS', 'RO', 'RR', 'SC', 'SP', 'SE', 'TO'];
let parsedCity = '';
let parsedState = '';
let parsedCountry = 'Brasil';
let foundBrazilPattern = false;
for (const part of parts) {
// FIX: Handle both hyphen and en-dash if possible, but let's test strict hyphen first as per code
if (part && part.includes('-')) {
const subParts = part.split('-').map(sp => sp.trim());
if (subParts.length >= 2) {
const potentialUF = subParts[subParts.length - 1].toUpperCase().substring(0, 2);
if (ufs.includes(potentialUF) && /^[A-Z]{2}$/.test(potentialUF)) {
parsedState = potentialUF;
subParts.pop();
parsedCity